CVE-2025-14070

Reviewify <= 1.0.7 - Missing Authorization to Authenticated (Contributor+) Arbitrary WooCommerce Coupon Creation

highMissing Authorization
7.5
CVSS Score
7.5
CVSS Score
high
Severity
1.0.8
Patched in
2d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
High
Integrity
None
Availability

Technical Details

Affected versions<=1.0.7
PublishedJanuary 6, 2026
Last updatedJanuary 8, 2026
Affected pluginreview-for-discount

Source Code

WordPress.org SVN
Research Plan
Unverified

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 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 via wp_ajax_send_test_email)
  • Vulnerable Hook: add_action( 'wp_ajax_send_test_email', ... )
  • Authentication: Authenticated (Contributor+)
  • Parameters (inferred from plugin purpose):
    • action: send_test_email
    • security or _ajax_nonce: (Nonce required)
    • coupon_type: (e.g., percent, fixed_cart)
    • coupon_amount: (e.g., 100 for 100%)
    • email: (Recipient for the "test" email, though the coupon is created in the database regardless)

3. Code Flow

  1. Registration: The plugin registers the AJAX handler in its main class or an AJAX handler class (likely includes/class-review-for-discount-ajax.php or similar) using add_action( 'wp_ajax_send_test_email', array( $this, 'send_test_email' ) );.
  2. Entry Point: A Contributor user sends a POST request to admin-ajax.php with action=send_test_email.
  3. Nonce Check: The function likely calls check_ajax_referer( 'reviewify_nonce', 'security' ) or similar.
  4. Authorization Gap: The function omits a call to current_user_can( 'manage_options' ) or current_user_can( 'manage_woocommerce' ).
  5. Sink: The code proceeds to call WooCommerce functions like new WC_Coupon() or directly manipulates the shop_coupon post type to create a new coupon based on the provided parameters.
  6. Execution: A new coupon is inserted into the wp_posts and wp_postmeta tables.

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.

  1. Identification: The plugin likely localizes the nonce in the WordPress admin area. Look for wp_localize_script calls in the source.
  2. Setup: Create a Contributor user.
  3. Execution:
    • Login as Contributor.
    • Navigate to /wp-admin/.
    • Use browser_eval to extract the nonce from the global JavaScript object.
    • JS Object Guess: window.reviewify_obj?.nonce or window.reviewify_admin_params?.security.

5. Exploitation Strategy

Step-by-Step Plan:

  1. Auth: Log in as a user with the Contributor role.
  2. Nonce Extraction: Navigate to the WP Admin dashboard and execute:
    browser_eval("reviewify_obj.nonce") (Verify actual object name in source).
  3. Request: Send a POST request to admin-ajax.php to 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:
    action=send_test_email&
    security=[NONCE]&
    email=attacker@example.com&
    coupon_type=percent&
    coupon_amount=100&
    discount_type=percent&
    coupon_code=EXPLOIT100
    
    (Note: Exact parameter names like coupon_code or coupon_amount must be verified against the plugin source code or by observing a legitimate "Test Email" request.)

6. Test Data Setup

  1. WooCommerce: Ensure WooCommerce is installed and active.
  2. Reviewify: Install and activate Reviewify <= 1.0.7.
  3. User: Create a user with username attacker and role contributor.
  4. 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 OK with success: true or 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

  1. CLI Check: Run wp wc coupon list --format=csv to see if a new coupon was created.
  2. Metadata Check: Verify the coupon details:
    wp post list --post_type=shop_coupon
    wp post meta list [COUPON_ID]
  3. 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_code is 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_post without sanitization, check for potential XSS in the coupon description or meta fields.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/includes/class-review-for-discount-ajax.php
+++ b/includes/class-review-for-discount-ajax.php
@@ -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.