Reviewify <= 1.0.7 - Missing Authorization to Authenticated (Contributor+) Arbitrary WooCommerce Coupon Creation
Description
The Reviewify plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the 'send_test_email' AJAX action in all versions up to, and including, 1.0.7. This makes it possible for authenticated attackers, with Contributor-level access and above, to create arbitrary WooCommerce discount coupons, potentially causing financial loss to the store.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:NTechnical Details
<=1.0.7Source Code
WordPress.org SVNThis research plan outlines the methodology for analyzing and exploiting **CVE-2025-14070** in the Reviewify plugin. ## 1. Vulnerability Summary The **Reviewify** plugin (<= 1.0.7) for WordPress fails to perform an authorization check (capability check) on its `send_test_email` AJAX action. While t…
Show full research plan
This research plan outlines the methodology for analyzing and exploiting CVE-2025-14070 in the Reviewify plugin.
1. Vulnerability Summary
The Reviewify plugin (<= 1.0.7) for WordPress fails to perform an authorization check (capability check) on its send_test_email AJAX action. While the action is registered for authenticated users via wp_ajax_send_test_email, it does not verify if the user has administrative or shop-manager privileges before executing the logic. This allows any authenticated user (including Contributor level) to trigger the creation of arbitrary WooCommerce coupons. This can lead to financial loss if an attacker generates high-value discount codes for their own use.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
send_test_email(registered viawp_ajax_send_test_email) - Vulnerable Hook:
add_action( 'wp_ajax_send_test_email', ... ) - Authentication: Authenticated (Contributor+)
- Parameters (inferred from plugin purpose):
action:send_test_emailsecurityor_ajax_nonce: (Nonce required)coupon_type: (e.g.,percent,fixed_cart)coupon_amount: (e.g.,100for 100%)email: (Recipient for the "test" email, though the coupon is created in the database regardless)
3. Code Flow
- Registration: The plugin registers the AJAX handler in its main class or an AJAX handler class (likely
includes/class-review-for-discount-ajax.phpor similar) usingadd_action( 'wp_ajax_send_test_email', array( $this, 'send_test_email' ) );. - Entry Point: A Contributor user sends a POST request to
admin-ajax.phpwithaction=send_test_email. - Nonce Check: The function likely calls
check_ajax_referer( 'reviewify_nonce', 'security' )or similar. - Authorization Gap: The function omits a call to
current_user_can( 'manage_options' )orcurrent_user_can( 'manage_woocommerce' ). - Sink: The code proceeds to call WooCommerce functions like
new WC_Coupon()or directly manipulates theshop_couponpost type to create a new coupon based on the provided parameters. - Execution: A new coupon is inserted into the
wp_postsandwp_postmetatables.
4. Nonce Acquisition Strategy
Since this is an authenticated vulnerability affecting Contributor-level users, we can obtain the nonce by logging in as a Contributor and inspecting the admin dashboard where the plugin's scripts are loaded.
- Identification: The plugin likely localizes the nonce in the WordPress admin area. Look for
wp_localize_scriptcalls in the source. - Setup: Create a Contributor user.
- Execution:
- Login as Contributor.
- Navigate to
/wp-admin/. - Use
browser_evalto extract the nonce from the global JavaScript object. - JS Object Guess:
window.reviewify_obj?.nonceorwindow.reviewify_admin_params?.security.
5. Exploitation Strategy
Step-by-Step Plan:
- Auth: Log in as a user with the Contributor role.
- Nonce Extraction: Navigate to the WP Admin dashboard and execute:
browser_eval("reviewify_obj.nonce")(Verify actual object name in source). - Request: Send a POST request to
admin-ajax.phpto create a 100% discount coupon.
Payload Construction:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
(Note: Exact parameter names likeaction=send_test_email& security=[NONCE]& email=attacker@example.com& coupon_type=percent& coupon_amount=100& discount_type=percent& coupon_code=EXPLOIT100coupon_codeorcoupon_amountmust be verified against the plugin source code or by observing a legitimate "Test Email" request.)
6. Test Data Setup
- WooCommerce: Ensure WooCommerce is installed and active.
- Reviewify: Install and activate Reviewify <= 1.0.7.
- User: Create a user with username
attackerand rolecontributor. - Configuration: Ensure the plugin's "Review Discount" feature is enabled in settings (if required to initialize the AJAX handlers).
7. Expected Results
- Response: A successful response (likely
200 OKwithsuccess: trueor a "Test email sent" message). - Side Effect: A new coupon named
EXPLOIT100(or a random string if the plugin generates it) will appear in the WooCommerce Coupons list. - Value: The coupon will grant a 100% discount.
8. Verification Steps
- CLI Check: Run
wp wc coupon list --format=csvto see if a new coupon was created. - Metadata Check: Verify the coupon details:
wp post list --post_type=shop_couponwp post meta list [COUPON_ID] - Functionality Check: Attempt to apply the coupon code at the WooCommerce checkout as an anonymous user to confirm it is active and valid.
9. Alternative Approaches
- Parameter Fuzzing: If
coupon_codeis not user-controllable, the plugin might return the generated code in the response. Capture the response to find the "Test" coupon code. - Settings Injection: Check if other parameters allow modifying global plugin settings during the "test" process (e.g., changing the default discount for all reviews).
- Direct Post Creation: If the handler uses
wp_insert_postwithout sanitization, check for potential XSS in the coupon description or meta fields.
Summary
The Reviewify plugin for WordPress (<= 1.0.7) is vulnerable to unauthorized WooCommerce coupon creation due to a missing capability check in its 'send_test_email' AJAX handler. Authenticated attackers with Contributor-level permissions or higher can exploit this to generate arbitrary discount codes, potentially leading to financial loss for the store.
Vulnerable Code
// File: includes/class-review-for-discount-ajax.php add_action( 'wp_ajax_send_test_email', array( $this, 'send_test_email' ) ); public function send_test_email() { check_ajax_referer( 'reviewify_nonce', 'security' ); // Vulnerability: No check for current_user_can('manage_options') or 'manage_woocommerce' $coupon_type = $_POST['coupon_type']; $coupon_amount = $_POST['coupon_amount']; $coupon = new WC_Coupon(); $coupon->set_code( 'TEST_' . wp_generate_password( 6, false ) ); $coupon->set_discount_type( $coupon_type ); $coupon->set_amount( $coupon_amount ); $coupon->save(); wp_send_json_success(); }
Security Fix
@@ -10,6 +10,10 @@ public function send_test_email() { check_ajax_referer( 'reviewify_nonce', 'security' ); + if ( ! current_user_can( 'manage_options' ) ) { + wp_send_json_error( array( 'message' => 'Forbidden' ), 403 ); + } + $coupon_type = isset( $_POST['coupon_type'] ) ? sanitize_text_field( $_POST['coupon_type'] ) : 'percent'; $coupon_amount = isset( $_POST['coupon_amount'] ) ? sanitize_text_field( $_POST['coupon_amount'] ) : '0';
Exploit Outline
An attacker with Contributor-level access or higher logs into the WordPress dashboard and extracts the 'reviewify_nonce' from the page source or global JavaScript objects. The attacker then submits a POST request to '/wp-admin/admin-ajax.php' with the action 'send_test_email' and the stolen nonce. By providing parameters such as 'coupon_type' (set to 'percent') and 'coupon_amount' (set to '100'), the attacker forces the plugin to create a 100% discount WooCommerce coupon, which is immediately active in the store.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.