CVE-2025-9816 is a critical stored cross-site scripting vulnerability in the widely used WP Statistics plugin (600k+ installs) that permits an attacker to persist a crafted User-Agent string into the plugin’s device model field and later execute arbitrary JavaScript inside the wp-admin interface when an administrator views the Devices → Device Models report. The root cause is a chain of weak protections: the UA string is lightly normalized by the parser but not fully sanitized or context-escaped before being stored and rendered, and the admin table renders the model value both into a text node and into an HTML attribute (title) without esc_html()
/esc_attr()
or equivalent context-aware escaping. Because administrators have high privileges and valid nonces in their browser context, any JavaScript that executes there can steal cookies, nonces, or trigger privileged actions—turning a seemingly low-signal analytics record into a direct path to full site takeover.
CVE | CVE-2025-9816 |
Plugin Version | WP Statistics <= 14.5.4 |
Critical | High |
All Time | 33 503 078 |
Active installations | 600 000+ |
Publicly Published | October 9, 2025 |
Last Updated | October 9, 2025 |
Researcher | Dmitrii Ignatyev |
PoC | Yes |
Exploit | No |
Reference | Our Telegram Channel -> https://t.me/cleantalk_researches/326 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-9816 https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/wp-statistics/wp-statistics-1454-unauthenticated-stored-cross-site-scripting-via-user-agent-header |
Plugin Security Certification by CleanTalk | ![]() |
Logo of the plugin | ![]() |
PSC by CleantalkJoin the community of developers who prioritize security. Highlight your plugin in the WordPress catalog.
Timeline
September 2, 2025 | Plugin testing and vulnerability detection in the WP Statistics – Simple, privacy-friendly Google Analytics alternative have been completed |
September 2, 2025 | I contacted the author of the plugin and provided a vulnerability PoC with a description and recommendations for fixing |
October 9, 2025 | Registered CVE-2025-9816 |
Discovery of the Vulnerability
During focused auditing of WP Statistics’ UA handling and reporting code, researchers observed that the plugin relies on an external UA parser to extract device/brand/model strings and then writes the parsed model into wp_statistics_visitor.model
(or an equivalent column). The parsing step performs minimal normalization—trimming, truncation, and the replacement of some control characters—but does not strip HTML metacharacters, quotes, or SVG/JS vectors that can survive parsing. Later, the admin UI builds a table of device models and inserts the stored string into both a visible cell and an element title attribute for hover tooltips, without calling esc_html()
for the text node or esc_attr()
for the attribute. Because the value is stored persistently and displayed in wp-admin where privileged code paths and nonces are available, the vulnerability combines storage, display-in-admin, and lack of contextual escaping into a classic and highly exploitable stored XSS.
Understanding of Stored XSS attack’s
Stored XSS has repeatedly been among the most damaging classes of WordPress vulnerabilities because the payload persists in the site database and executes in privileged contexts when an administrator or editor visits an affected page. In the past, similar attack chains have led to complete site takeover: plugin fields written to post titles, user metadata, or analytics tables have been leveraged to execute admin-level JavaScript, steal authentication cookies, and perform administrative actions programmatically. The distinguishing risk in WP Statistics is the telemetry-like nature of the input (User-Agent), which looks innocuous and commonly appears in logs. Attackers can weaponize ordinary HTTP headers sent by automated scanners or crafted clients; unlike forms that require some interaction, analytics endpoints often accept and store headers from any visitor, including unauthenticated bot traffic, which makes mass automatic injection and wide-scale compromise simple to script.
Exploiting the Stored XSS Vulnerability
To exploit CVE-2025-9816, an attacker without any cookies:
POC:
POST /wordpress/index.php/wp-json/wp-statistics/v2/hit HTTP/1.1 Host: test.test User-Agent: Mozilla/5.0 (Linux; Android 11; HP; X"><svg onload=alert`1337`>) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0 Mobile Safari/537.36 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/x-www-form-urlencoded Content-Length: 195 Origin: http://test.test DNT: 1 Sec-GPC: 1 Connection: keep-alive Referer: http://test.test/wordpress/index.php/2025/08/05/asd-3/ Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin wp_statistics_hit=1&source_type=post&source_id=1015&search_query=&signature=1701d86fd78772afa197fc2f8123b&endpoint=hit&referred=&page_uri=L3d123
____
The real-world impact is severe and immediate. Because WP Statistics is a telemetry/analytics plugin installed on hundreds of thousands of sites, attackers can automate payload injection at scale by sending millions of requests with unique UA payloads and then scanning for admin panels to load the report and trigger exploitation. On targeted attacks, an adversary could craft payloads specifically designed to survive the UA parser’s normalization rules and to bypass simple WAF signatures, thereby achieving stealthy persistence. Once administrative nonces and cookies are exfiltrated, lateral movement and long-term backdoors become trivial: attackers may install malicious plugins, modify theme files to include web shells, or export private data. For multi-site or agency-managed WordPress installations where admins log into several client dashboards, a single exploited analytics entry could cascade into compromise of multiple sites and credentials, making the vulnerability both high-impact and attractive to opportunistic and targeted attackers.
Recommendations for Improved Security
To remediate CVE-2025-9816, WP Statistics must treat any parser-derived data as untrusted input and enforce both sanitization at write-time and context-aware escaping at read-time. At minimum, the plugin should apply strict normalization and validation to model strings before storage: remove or encode control and metacharacters, reject strings containing <
, >
, "
, '
, or sequences that look like HTML/event attributes, and truncate to a safe length. When rendering in the admin UI, always use esc_html()
for text nodes and esc_attr()
for attribute values, and consider wp_kses()
with an explicit allowlist if any HTML is ever accepted (which is strongly discouraged for analytics fields). Additionally, implement defensive layers such as output encoding on database reads, server-side filtering of incoming UA strings (e.g., reject or canonicalize suspicious headers), and logging/alerting when new or anomalous device models are stored. Site owners should update to a patched release immediately, purge suspicious device model entries, reset admin sessions and keys if exploitation is suspected, and monitor wp-admin access logs for unusual activity. Finally, consider placing administrative analytics pages behind stronger access controls or rate limits so that automated mass exploitation is harder to perform.
By taking proactive measures to address Stored XSS vulnerabilities like CVE-2025-9816 WordPress website owners can enhance their security posture and safeguard against potential exploitation. Stay vigilant, stay secure.
#WordPressSecurity #Stored XSS #WebsiteSafety #StayProtected #HighVulnerability
Use CleanTalk solutions to improve the security of your website
Dmitrii I.