CVE-2026-9014

WP Promoter <= 1.3 - Missing Authorization to Unauthenticated Statistics Reset via wpp-reset_stats AJAX Action

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The WP Promoter plugin for WordPress is vulnerable to unauthorized modification of data due to a missing capability check on the reset_stats() function in versions up to, and including, 1.3. The function is hooked to both the wp_ajax_wpp-reset_stats and wp_ajax_nopriv_wpp-reset_stats actions and contains no authentication, authorization, or nonce validation. This makes it possible for unauthenticated attackers to reset the plugin's bar and popup statistics by deleting the wpp_bar and wpp_popup options.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.3
PublishedMay 26, 2026
Last updatedMay 27, 2026
Affected pluginwp-promoter
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-9014 (WP Promoter) ## 1. Vulnerability Summary The **WP Promoter** plugin (<= 1.3) contains a missing authorization vulnerability in its statistics reset functionality. The function `reset_stats()` is incorrectly exposed to unauthenticated users via the `wp_a…

Show full research plan

Exploitation Research Plan - CVE-2026-9014 (WP Promoter)

1. Vulnerability Summary

The WP Promoter plugin (<= 1.3) contains a missing authorization vulnerability in its statistics reset functionality. The function reset_stats() is incorrectly exposed to unauthenticated users via the wp_ajax_nopriv_wpp-reset_stats hook. Because the function lacks any capability checks (current_user_can) or CSRF protection (nonces), any unauthenticated attacker can trigger a reset of the plugin's bar and popup statistics by deleting the underlying WordPress options.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: wpp-reset_stats
  • HTTP Method: POST (standard for AJAX, though GET might also work depending on implementation)
  • Parameters: action=wpp-reset_stats
  • Authentication: None required (vulnerable via nopriv hook)
  • Preconditions: The plugin must be active.

3. Code Flow

Based on the vulnerability description, the execution flow is as follows:

  1. Entry Point: An HTTP request hits admin-ajax.php with the parameter action=wpp-reset_stats.
  2. Hook Execution: WordPress triggers the wp_ajax_nopriv_wpp-reset_stats hook (for logged-out users) or wp_ajax_wpp-reset_stats (for logged-in users).
  3. Target Function: The plugin's reset_stats() function (inferred) is called.
  4. Missing Check: The function fails to call check_ajax_referer() or current_user_can('manage_options').
  5. The Sink: The function executes delete_option('wpp_bar') and delete_option('wpp_popup') (quoted from description).
  6. Response: The plugin likely exits using wp_die() or wp_send_json_success().

4. Nonce Acquisition Strategy

According to the vulnerability description, the reset_stats() function contains no nonce validation.

  • Action String: Not applicable.
  • Bypass: No bypass is necessary as the security check is entirely absent in the vulnerable versions.

Note: If testing reveals a nonce is actually present but the description was slightly inaccurate, the agent should search for wp_localize_script in the plugin source to identify the JS variable name (likely something like wpp_vars or wpp_ajax) and use browser_eval to extract it.

5. Exploitation Strategy

The goal is to demonstrate that an unauthenticated request can successfully delete the statistics options.

Step-by-step Plan:

  1. Prepare Request: Construct a POST request to the AJAX endpoint.
  2. Execute Request: Use http_request to send the payload.
  3. Payload Structure:
    • URL: {{base_url}}/wp-admin/admin-ajax.php
    • Method: POST
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body: action=wpp-reset_stats

6. Test Data Setup

To verify the impact, we must first ensure the statistics options exist and contain data.

  1. Initialize Options: Use WP-CLI to create the options that the plugin uses for statistics.
    wp option update wpp_bar '{"views": 100, "clicks": 50}' --format=json
    wp option update wpp_popup '{"views": 200, "clicks": 75}' --format=json
    
  2. Confirm Setup:
    wp option get wpp_bar
    wp option get wpp_popup
    

7. Expected Results

  • HTTP Response: The server should return a 200 OK status. The body might be empty, 0, 1, or a JSON success message depending on how the plugin terminates the AJAX call.
  • Database State: The options wpp_bar and wpp_popup should no longer exist in the wp_options table (or should be reset to default/empty values).

8. Verification Steps

After the http_request tool completes, verify the deletion using WP-CLI:

  1. Check Options:
    wp option get wpp_bar
    wp option get wpp_popup
    
  2. Success Criteria: If WP-CLI returns an error like Error: Could not get 'wpp_bar' option., the exploit is successful because the unauthenticated request triggered the delete_option() call.

9. Alternative Approaches

  • Method Variation: If POST fails, attempt a GET request: /wp-admin/admin-ajax.php?action=wpp-reset_stats.
  • Parameter Variation: If the options are not deleted, check the source code for a specific "type" parameter (e.g., action=wpp-reset_stats&type=bar) that might be required to target a specific option, though the description implies both are deleted.
  • Capability Check Verification: Attempt the request with a low-privileged user (Subscriber) to confirm that no capability checks are enforced for any level of user.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Promoter plugin for WordPress (versions <= 1.3) allows unauthenticated attackers to reset plugin statistics due to missing authorization and CSRF checks in its AJAX handler. The `reset_stats` function is hooked to the `nopriv` AJAX action and fails to verify user permissions or nonces before deleting key configuration options.

Vulnerable Code

// File: wp-promoter.php (inferred from plugin slug)

add_action( 'wp_ajax_wpp-reset_stats', 'reset_stats' );
add_action( 'wp_ajax_nopriv_wpp-reset_stats', 'reset_stats' );

function reset_stats() {
    delete_option('wpp_bar');
    delete_option('wpp_popup');
    wp_die();
}

Security Fix

--- wp-promoter.php
+++ wp-promoter.php
@@ -1,6 +1,9 @@
 add_action( 'wp_ajax_wpp-reset_stats', 'reset_stats' );
-add_action( 'wp_ajax_nopriv_wpp-reset_stats', 'reset_stats' );
 
 function reset_stats() {
+    check_ajax_referer( 'wpp-reset-stats-nonce', 'nonce' );
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_die( -1 );
+    }
     delete_option('wpp_bar');
     delete_option('wpp_popup');
     wp_die();

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker can send a crafted POST request to the WordPress AJAX endpoint. 1. Target Endpoint: /wp-admin/admin-ajax.php 2. Authentication: None required. 3. Method: POST 4. Payload: A form-urlencoded body containing the parameter 'action=wpp-reset_stats'. 5. Mechanism: Because the plugin registers the 'wpp-reset_stats' action with 'wp_ajax_nopriv_', any logged-out visitor can trigger the handler. Since the handler lacks capability checks (current_user_can) and CSRF protection (check_ajax_referer), the server will execute delete_option() calls for the statistics data ('wpp_bar' and 'wpp_popup') upon receiving the request.

Check if your site is affected.

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