Introduction
An unauthenticated stored cross-site scripting vulnerability in the WP Statistics plugin for WordPress lets any external visitor seed a malicious JavaScript payload into the site's analytics database, where it silently waits for an administrator to open a dashboard chart. On the estimated millions of WordPress sites that use WP Statistics as a privacy-focused alternative to Google Analytics, a single crafted HTTP request from an anonymous attacker can lead to script execution inside a privileged admin session.
WP Statistics, maintained by VeronaLabs, is one of the most popular self-hosted analytics plugins for WordPress. It gives site owners visitor tracking, referral analysis, and traffic reporting without sending data to third-party services. Because so many WordPress installations run it, a vulnerability in this plugin has a wide reach.
Technical information
CVE-2026-5231 is classified as CWE-79 (Stored Cross-Site Scripting) and carries a CVSS 3.1 score of 7.2 with the vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N. The exploit chain spans two components: a server-side referral parser and a client-side chart renderer.
Server-side root cause: unsanitized input in ReferralsParser.php
The vulnerability originates in the parse() method of ReferralsParser.php at line 62. When the plugin processes incoming referral data, it checks whether the referral channel definition uses a wildcard domain (*). If it does, the plugin takes the raw utm_source query parameter value from the request and stores it directly as the channel's display name. In version 14.16.4, this assignment had no sanitization at all:
// v14.16.4 (VULNERABLE) if ($channelDomain == '*') { $channels[$key]['name'] = $value; }
Any string provided in the utm_source parameter, including HTML and JavaScript, is therefore written verbatim to the database as the source_name.
Client-side root cause: innerHTML injection in chart.js
The second half of the chain is in chart.js, which builds the admin dashboard charts. Both the externalTooltipHandler (tooltip rendering) and updateLegend (legend rendering) functions construct HTML with template literals and inject it into the DOM through innerHTML. In version 14.16.4, dataset.label, which carries the attacker-controlled source_name, was interpolated directly without any escaping:
// v14.16.4 (VULNERABLE) — tooltip handler innerHtml += `... ${dataset.label} ...`;
// v14.16.4 (VULNERABLE) — legend builder legendItem.innerHTML = `${dataset.label} ...`;
Attack flow
-
An unauthenticated attacker sends one or more HTTP requests to any public-facing page on the target WordPress site. These requests include a crafted
utm_sourceparameter containing a JavaScript payload (for example, an<img>tag with anonerrorhandler or a<script>block). -
The WP Statistics referral parser processes the request. Because the channel definition uses a wildcard domain, the parser stores the raw
utm_sourcevalue in the database as thesource_namewithout sanitization. -
The malicious payload sits dormant in the database.
-
When a WordPress administrator opens the Referrals Overview or Social Media analytics pages in the admin dashboard, the chart renderer reads the stored
source_nameand inserts it into the chart legend or tooltip markup throughinnerHTML. -
The browser parses the injected HTML and executes the attacker's JavaScript in the context of the administrator's authenticated session.
The CVSS scope change indicator (S:C) reflects that the payload is seeded in the unauthenticated, public-facing context but executes in the privileged admin context. The CVSS vector marks Confidentiality and Integrity impacts as Low, but in practice, arbitrary JavaScript execution in an admin session can be used to create new admin accounts, modify site content, or install backdoor plugins.
The attack requires no privileges (PR:N) and no user interaction for the initial seeding (UI:N), and its complexity is low (AC:L). That makes it trivially automatable at scale.
Patch information
WP Statistics version 14.16.5, released on April 11, 2026, fixes the vulnerability. The patch takes a defense-in-depth approach and addresses the issue at both the server-side input layer and the client-side output layer. The changeset is tracked between WordPress plugin SVN revisions 3483860 (v14.16.4) and 3503795 (v14.16.5).
Server-side fix: input sanitization in ReferralsParser.php
In version 14.16.5, the raw utm_source value is wrapped with WordPress's built-in sanitize_text_field() function before it is stored:
// v14.16.5 (PATCHED) if ($channelDomain == '*') { $channels[$key]['name'] = sanitize_text_field($value); }
sanitize_text_field() strips all HTML tags, removes extra whitespace and line breaks, and encodes special characters. A malicious utm_source value such as <img src=x onerror=alert(1)> would be reduced to harmless plain text before it reaches the database.
Client-side fix: output escaping in chart.js
In version 14.16.5, both the externalTooltipHandler and updateLegend functions pass dataset.label through a new escape function before insertion:
// v14.16.5 (PATCHED) — tooltip handler innerHtml += `... ${wps_js.escapeHtml(dataset.label)} ...`;
// v14.16.5 (PATCHED) — legend builder legendItem.innerHTML = `${wps_js.escapeHtml(dataset.label)} ...`;
New helper: wps_js.escapeHtml() in helper.js
The wps_js.escapeHtml() function did not exist in version 14.16.4. The patch adds it to helper.js. It replaces the five most critical HTML special characters (&, <, >, ", ') with character entities, which is sufficient to prevent HTML injection in an innerHTML context.
With both fixes in place, (1) sanitize_text_field() strips the malicious payload on the server before storage, and (2) if a tainted value already exists in the database from records written before the patch was applied, wps_js.escapeHtml() still escapes it before the admin-facing chart UI renders it.
Affected systems and versions
All versions of the WP Statistics plugin for WordPress up to and including 14.16.4 are affected. The vulnerability is present in any WordPress installation running a vulnerable version of the plugin with referral tracking active (the default configuration).
The fix is available in version 14.16.5 and later.
References
- NVD Entry for CVE-2026-5231
- Wordfence Vulnerability Advisory
- WP Statistics Plugin Developer Changelog
- ReferralsParser.php (v14.16.4, vulnerable)
- ReferralsParser.php (v14.16.5, patched)
- chart.js (v14.16.4, vulnerable)
- chart.js (v14.16.5, patched)
- helper.js (v14.16.5, new escapeHtml function)
- helper.js (v14.16.4, no escapeHtml function)
- WordPress Trac: ReferralsParser.php in tags/14.16.4
- WordPress Trac: chart.js in tags/14.16.4
- WordPress Trac: Patch Changeset (3483860 to 3503795)
- CISA Known Exploited Vulnerabilities Catalog



