WP Statistics <= 14.16.4 - Missing Authorization to Authenticated (Subscriber+) Sensitive Information Exposure and Privacy Audit Manipulation
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:NTechnical Details
<=14.16.4What Changed in the Fix
Changes introduced in v14.16.5
Source Code
WordPress.org SVN# 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 validwp_restnonce).
3. Code Flow
- Entry Point: A request is sent to
admin-ajax.phpwithaction=wp_statistics_.... - Hook Registration: The plugin registers AJAX handlers using
add_action('wp_ajax_...', ...). - Nonce Verification: The handler calls
check_ajax_referer('wp_rest', 'wps_nonce')(as indicated by the vulnerability description andassets/dev/javascript/helper.jsusage). - 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. - 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.
- Identification: The JS source
assets/dev/javascript/helper.jsshows the plugin expects the nonce in a variable:wps_js.global.rest_api_nonce. - Localization: This is localized into the page as part of the
wps_globalobject (seen inassets/dev/javascript/config.js). - Acquisition Steps:
- Log in as a Subscriber.
- Navigate to the WordPress Dashboard (
/wp-admin/index.php). - Use
browser_evalto extract the nonce:window.wps_global?.rest_api_nonce || window.wpApiSettings?.nonce - Note: In standard WordPress,
window.wpApiSettings.noncealso contains thewp_restnonce 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
- User Creation: Create a subscriber user
attacker. - Plugin Content: Ensure the plugin has some data. Generate at least one visit by navigating the site as an anonymous user.
- 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 OKwith a JSON body:{"success":true,"data":{...}}. - Payload Contents: The
dataobject 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 Forbiddenif a capability check were present, but they instead receive the administrative data.
8. Verification Steps
- Check Options via CLI:
Verify if values changed after thewp option get wp_statistics_privacy_details (or similar option name)updatePrivacyStatusexploit. - Inspect Response: Verify the JSON response of
wp_statistics_get_filterscontains real user data (e.g., IDs from thewp_userstable).
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 inassets/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_globalobject will likely be enqueued there to support frontend stats/charts. - Dismiss Notices: Try
action=wp_statistics_dismiss_notices&wps_nonce=...¬ice=allto see if administrative UI elements disappear for all users.
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
@@ -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.