CVE-2026-3488

WP Statistics <= 14.16.4 - Missing Authorization to Authenticated (Subscriber+) Sensitive Information Exposure and Privacy Audit Manipulation

mediumMissing Authorization
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
14.16.5
Patched in
1d
Time to patch

Description

The WP Statistics plugin for WordPress is vulnerable to Missing Authorization in all versions up to, and including, 14.16.4. This is due to missing capability checks on multiple AJAX handlers including `wp_statistics_get_filters`, `wp_statistics_getPrivacyStatus`, `wp_statistics_updatePrivacyStatus`, and `wp_statistics_dismiss_notices`. These endpoints only verify a `wp_rest` nonce via `check_ajax_referer()` but do not enforce any capability checks such as `current_user_can()` or the plugin's own `User::Access()` method. Since the `wp_rest` nonce is available to all authenticated WordPress users, this makes it possible for authenticated attackers, with Subscriber-level access and above, to access sensitive analytics data (user IDs, usernames, emails, visitor tracking data), retrieve and modify privacy audit compliance status, and dismiss administrative notices.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=14.16.4
PublishedApril 16, 2026
Last updatedApril 17, 2026
Affected pluginwp-statistics

What Changed in the Fix

Changes introduced in v14.16.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-3488 ## 1. Vulnerability Summary The **WP Statistics** plugin (<= 14.16.4) contains a missing authorization vulnerability within several AJAX handlers. While these handlers correctly verify a WordPress nonce (specifically the `wp_rest` action nonce), they fai…

Show full research plan

Exploitation Research Plan - CVE-2026-3488

1. Vulnerability Summary

The WP Statistics plugin (<= 14.16.4) contains a missing authorization vulnerability within several AJAX handlers. While these handlers correctly verify a WordPress nonce (specifically the wp_rest action nonce), they fail to implement any capability checks (e.g., current_user_can() or the plugin's internal User::Access() method).

Because the wp_rest nonce is automatically generated and provided to all authenticated users (including low-privileged Subscribers) for standard WordPress functionality, an authenticated attacker can invoke these administrative functions to leak sensitive analytics data, modify privacy audit settings, and dismiss administrative notices.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Method: POST
  • Authentication: Required (Subscriber-level or higher)
  • Vulnerable Actions:
    • wp_statistics_get_filters (Data Exposure)
    • wp_statistics_getPrivacyStatus (Info Leak)
    • wp_statistics_updatePrivacyStatus (Unauthorized Modification)
    • wp_statistics_dismiss_notices (Action Unauthorized)
  • Key Parameter: wps_nonce (containing a valid wp_rest nonce).

3. Code Flow

  1. Entry Point: A request is sent to admin-ajax.php with action=wp_statistics_....
  2. Hook Registration: The plugin registers AJAX handlers using add_action('wp_ajax_...', ...).
  3. Nonce Verification: The handler calls check_ajax_referer('wp_rest', 'wps_nonce') (as indicated by the vulnerability description and assets/dev/javascript/helper.js usage).
  4. Vulnerable Sink: The handler proceeds to execute logic (e.g., fetching database records of visitors/users or updating options) without calling current_user_can('manage_options') or equivalent.
  5. Data Return: The sensitive data is returned via wp_send_json_success().

4. Nonce Acquisition Strategy

The plugin uses the standard WordPress REST API nonce for its AJAX operations. This nonce is globally available to any logged-in user in the WordPress admin dashboard.

  1. Identification: The JS source assets/dev/javascript/helper.js shows the plugin expects the nonce in a variable: wps_js.global.rest_api_nonce.
  2. Localization: This is localized into the page as part of the wps_global object (seen in assets/dev/javascript/config.js).
  3. Acquisition Steps:
    • Log in as a Subscriber.
    • Navigate to the WordPress Dashboard (/wp-admin/index.php).
    • Use browser_eval to extract the nonce:
      window.wps_global?.rest_api_nonce || window.wpApiSettings?.nonce
      
    • Note: In standard WordPress, window.wpApiSettings.nonce also contains the wp_rest nonce and is often equivalent.

5. Exploitation Strategy

Step 1: Data Exposure (Sensitive Filters/Users)

Action: wp_statistics_get_filters

  • Goal: Retrieve a list of filters that may include user IDs, usernames, or site structure.
  • Request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=wp_statistics_get_filters&wps_nonce=[EXTRACTED_NONCE]

Step 2: Privacy Audit Manipulation

Action: wp_statistics_updatePrivacyStatus

  • Goal: Disable or modify privacy features.
  • Request (Inferred parameters):
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=wp_statistics_updatePrivacyStatus&wps_nonce=[EXTRACTED_NONCE]&status=0

(Note: If status is not the correct key, we will attempt to "get" first to see the structure).

Step 3: Privacy Status Exposure

Action: wp_statistics_getPrivacyStatus

  • Goal: Read the current compliance and audit status.
  • Request:
POST /wp-admin/admin-ajax.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=wp_statistics_getPrivacyStatus&wps_nonce=[EXTRACTED_NONCE]

6. Test Data Setup

  1. User Creation: Create a subscriber user attacker.
  2. Plugin Content: Ensure the plugin has some data. Generate at least one visit by navigating the site as an anonymous user.
  3. Settings: Ensure some privacy settings are configured in the WP Statistics settings page so they can be read/modified.

7. Expected Results

  • Success: The server returns a 200 OK with a JSON body: {"success":true,"data":{...}}.
  • Payload Contents: The data object will contain sensitive environment details, such as visitor logs, user lists (IDs/Emails), or the current plugin configuration/privacy status.
  • Unauthorized Status: The attacker (Subscriber) should have received 403 Forbidden if a capability check were present, but they instead receive the administrative data.

8. Verification Steps

  1. Check Options via CLI:
    wp option get wp_statistics_privacy_details (or similar option name)
    
    Verify if values changed after the updatePrivacyStatus exploit.
  2. Inspect Response: Verify the JSON response of wp_statistics_get_filters contains real user data (e.g., IDs from the wp_users table).

9. Alternative Approaches

If the wp_rest nonce is not available on the dashboard for Subscribers:

  • Search for Shortcodes: Identify any plugin shortcode (e.g., [wpstatistics...] seen in assets/js/tinymce.min.js) and place it on a post.
  • Navigate to Post: Browse to that post as the Subscriber. The plugin's JS and localized wps_global object will likely be enqueued there to support frontend stats/charts.
  • Dismiss Notices: Try action=wp_statistics_dismiss_notices&wps_nonce=...&notice=all to see if administrative UI elements disappear for all users.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Statistics plugin for WordPress (<= 14.16.4) fails to implement capability checks in several AJAX handlers, relying solely on a REST API nonce for verification. This allows authenticated users with subscriber-level permissions to access sensitive analytics data, modify privacy settings, and dismiss administrative notifications.

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-statistics/14.16.4/assets/dev/javascript/chart.js /home/deploy/wp-safety.org/data/plugin-versions/wp-statistics/14.16.5/assets/dev/javascript/chart.js
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-statistics/14.16.4/assets/dev/javascript/chart.js	2025-12-01 07:17:10.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-statistics/14.16.5/assets/dev/javascript/chart.js	2026-04-11 07:13:12.000000000 +0000
@@ -125,7 +125,7 @@
                 <div class="current-data">
                     <div>
                         <span class="current-data__color" style="background-color: ${dataset.hoverPointBackgroundColor};"></span>
-                        ${dataset.label}
+                        ${wps_js.escapeHtml(dataset.label)}
                     </div>
                     <span class="current-data__value">${value.toLocaleString()}</span>
                 </div>`;
@@ -496,7 +496,7 @@
                 </div>` : '';
 
                 legendItem.innerHTML = `
-                <span>${dataset.label}</span>
+                <span>${wps_js.escapeHtml(dataset.label)}</span>
                 <div>
                     <div class="current-data">
                         <span class="wps-postbox-chart--item--color" style="border-color: ${dataset.borderColor}"></span>
... (truncated)

Exploit Outline

1. Authenticate to the WordPress site as a user with at least Subscriber-level permissions. 2. Access the dashboard and extract the WordPress REST API nonce (wp_rest action), which the plugin localizes in the `wps_global.rest_api_nonce` or `wpApiSettings.nonce` JavaScript objects. 3. Construct a POST request to `/wp-admin/admin-ajax.php` with the `wps_nonce` parameter set to the extracted nonce. 4. Set the `action` parameter to a vulnerable administrative handler, such as `wp_statistics_get_filters` (to retrieve sensitive user and visitor data) or `wp_statistics_updatePrivacyStatus` (to modify the site's privacy configuration). 5. Observe that the server processes the request and returns administrative information or modifies settings, despite the attacker lacking the necessary capabilities, because the plugin only verifies the nonce and not the user's role.

Check if your site is affected.

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