CVE-2026-15780 Overview
CVE-2026-15780 is a stored Cross-Site Scripting (XSS) vulnerability affecting the WP Statistics plugin for WordPress, a privacy-friendly Google Analytics alternative. The flaw affects all versions up to and including 14.16.8 and stems from insufficient input sanitization and output escaping on the utm_campaign parameter. Unauthenticated attackers can inject arbitrary JavaScript into stored records that execute when administrators or users view affected report pages. The issue is tracked as [CWE-79] (Improper Neutralization of Input During Web Page Generation).
Critical Impact
Unauthenticated attackers can plant persistent JavaScript payloads that execute in the browser context of any user viewing WP Statistics visitor or referral reports, enabling session theft, admin account takeover, and further site compromise.
Affected Products
- WP Statistics – Simple, privacy-friendly Google Analytics alternative plugin for WordPress
- All versions up to and including 14.16.8
- WordPress sites exposing the /wp-statistics/v2/hit REST endpoint
Discovery Timeline
- 2026-08-19 - CVE-2026-15780 published to NVD
- 2026-08-20 - Last updated in NVD database
Technical Details for CVE-2026-15780
Vulnerability Analysis
The vulnerability resides in the WP Statistics plugin's request URI handling logic. The plugin exposes a public REST endpoint at /wp-statistics/v2/hit used to record visitor telemetry. Because the required signature (nonce) is embedded on the public homepage, unauthenticated attackers can obtain it and submit crafted hits.
The getRequestUri() function in class-wp-statistics-helper.php accepts a base64-encoded page_uri POST parameter and decodes it without applying URL sanitization. The decoded value overrides the previously sanitized $_SERVER['REQUEST_URI'], letting a malicious utm_campaign value bypass sanitization. The tainted value is written to the database and later rendered in the visitors and referred-visitors report tables without adequate output escaping, resulting in stored XSS.
Root Cause
The root cause is a trust boundary violation. The plugin re-decodes attacker-controlled base64 data after the point at which sanitization normally occurs, then treats it as an authoritative request URI. Downstream code paths in class-wp-statistics-hits.php propagate the unsanitized value into stored records queried by the admin dashboard.
Attack Vector
An unauthenticated remote attacker retrieves the public hit signature from the site homepage. The attacker then sends a POST request to /wp-statistics/v2/hit containing a base64-encoded page_uri that includes a malicious utm_campaign query parameter carrying an XSS payload. When an administrator later views the visitor reports, the injected script executes in their authenticated session.
// Patched code from includes/class-wp-statistics-helper.php
// Source: https://github.com/wp-statistics/wp-statistics/commit/6ab74427778b89c9e88471e9dd7407d9055a9b34
public static function getRequestUri()
{
if (self::is_rest_request() and isset($_REQUEST['page_uri'])) {
- return base64_decode($_REQUEST['page_uri']);
+ return sanitize_url(base64_decode($_REQUEST['page_uri']));
}
return sanitize_url(wp_unslash($_SERVER['REQUEST_URI']));
}
// Patched code from includes/class-wp-statistics-hits.php
public function set_page_uri($page_uri)
{
- return isset($this->rest_hits->page_uri) ? base64_decode($this->rest_hits->page_uri) : $page_uri;
+ return isset($this->rest_hits->page_uri) ? sanitize_url(base64_decode($this->rest_hits->page_uri)) : $page_uri;
}
The patch applies sanitize_url() to the base64-decoded page_uri in both the helper and hits classes, preventing arbitrary script content from being persisted.
Detection Methods for CVE-2026-15780
Indicators of Compromise
- POST requests to /wp-json/wp-statistics/v2/hit containing a page_uri parameter with base64-encoded content decoding to strings such as utm_campaign=<script> or javascript: URIs.
- Records in the WP Statistics visitor and referrer tables (for example, wp_statistics_visitor) whose stored URI or campaign fields contain HTML tags, on*= event handlers, or %3Cscript sequences.
- Unexpected outbound requests from administrator browsers immediately after opening WP Statistics report pages.
Detection Strategies
- Inspect web server access logs for high-volume or scripted POSTs to /wp-statistics/v2/hit originating from a small set of IP addresses within a short window.
- Query the WP Statistics database tables for stored values containing angle brackets, javascript:, or common XSS payload markers.
- Alert on WordPress admin sessions that trigger anomalous DOM activity or cross-origin requests when navigating to WP Statistics dashboards.
Monitoring Recommendations
- Enable Web Application Firewall (WAF) logging for the WP Statistics REST namespace and retain full request bodies for forensic review.
- Monitor administrator user agents and session tokens for signs of hijack following visits to plugin report pages.
- Track plugin version inventory across WordPress fleets to identify hosts still running WP Statistics 14.16.8 or earlier.
How to Mitigate CVE-2026-15780
Immediate Actions Required
- Update the WP Statistics plugin to the version containing commit 6ab74427778b89c9e88471e9dd7407d9055a9b34 (released after 14.16.8) on every WordPress site in the environment.
- Audit the WP Statistics database tables and purge any rows whose URI or campaign fields contain HTML or scripting content.
- Rotate credentials and session tokens for administrators who accessed WP Statistics reports while the plugin was vulnerable.
Patch Information
The vendor addressed the issue by wrapping the base64-decoded page_uri with sanitize_url() in both getRequestUri() and set_page_uri(). The patch is available in the GitHub commit 6ab7442 and via the WordPress.org plugin repository. Detailed vulnerability analysis is published in the Wordfence Threat Intelligence advisory.
Workarounds
- Deploy a WAF rule that blocks POST requests to /wp-statistics/v2/hit where the base64-decoded page_uri contains <, >, or javascript: substrings.
- Restrict access to the /wp-json/wp-statistics/ REST namespace to trusted IP ranges until the patched version is deployed.
- Temporarily deactivate the WP Statistics plugin on sites that cannot be updated immediately.
# Example ModSecurity rule to block XSS payloads in the page_uri parameter
SecRule REQUEST_URI "@beginsWith /wp-json/wp-statistics/v2/hit" \
"id:1015780,phase:2,deny,status:403,log,msg:'CVE-2026-15780 WP Statistics XSS attempt',\
chain"
SecRule ARGS:page_uri "@rx (?i)(<script|javascript:|on\w+\s*=|%3Cscript)" \
"t:none,t:base64Decode,t:urlDecodeUni,t:lowercase"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

