WP Visitor Statistics (Real Time Traffic) <= 8.4 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'height' Shortcode Attribute
Description
The WP Visitor Statistics (Real Time Traffic) plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's 'wsm_showDayStatsGraph' shortcode in all versions up to, and including, 8.4 due to insufficient input sanitization and output escaping on user supplied attributes. This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=8.4What Changed in the Fix
Changes introduced in v8.5
Source Code
WordPress.org SVN# Research Plan: CVE-2026-4303 - Stored XSS in WP Visitor Statistics ## 1. Vulnerability Summary The **WP Visitor Statistics (Real Time Traffic)** plugin (versions <= 8.4) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `[wsm_showDayStatsGraph]` shortcode. The vulnerability exists be…
Show full research plan
Research Plan: CVE-2026-4303 - Stored XSS in WP Visitor Statistics
1. Vulnerability Summary
The WP Visitor Statistics (Real Time Traffic) plugin (versions <= 8.4) is vulnerable to Stored Cross-Site Scripting (XSS) via the [wsm_showDayStatsGraph] shortcode. The vulnerability exists because the plugin fails to sanitize or escape the height attribute supplied in the shortcode before echoing it into the page's HTML. Authenticated attackers with Contributor privileges or higher can embed this shortcode in a post. When a user (typically an administrator) views or previews the post, the malicious script executes in their browser context.
2. Attack Vector Analysis
- Shortcode:
[wsm_showDayStatsGraph] - Vulnerable Attribute:
height - Authentication Level: Contributor (or higher)
- Injection Point: Post or Page content.
- Target: Administrators or any users viewing the post containing the shortcode.
- Vulnerability Type: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79).
3. Code Flow
- Entry Point: A Contributor-level user saves a post containing the shortcode:
[wsm_showDayStatsGraph height='"><script>alert(document.domain)</script>']. - Shortcode Registration: In
includes/wsm_statistics.php, the classwsmStatisticsregisters the shortcode:
(Note:add_shortcode(WSM_PREFIX . '_showDayStatsGraph', array($this, WSM_PREFIX . '_showDayStatsGraph'));WSM_PREFIXis defined aswsminwp-stats-manager.php). - Processing: When the post is rendered (via
the_contentfilter callingdo_shortcode), WordPress calls the methodwsmStatistics::wsm_showDayStatsGraph($atts). - Parsing Attributes: The method uses
shortcode_attsto parse the user-supplied attributes, includingheight. - Vulnerable Sink: The attribute
heightis concatenated directly into an HTML string (likely within astyleattribute or adata-attribute for a chart container) and returned/echoed without being passed throughesc_attr()oresc_html(). - Execution: The browser renders the HTML, encounters the unescaped breakout characters (e.g.,
">), and executes the injected<script>tag.
4. Nonce Acquisition Strategy
Shortcodes in WordPress do not require a nonce for execution when rendered via the_content. The attack relies on the standard WordPress post-saving mechanism.
If the shortcode handler requires a nonce for an internal AJAX call triggered by the shortcode (unlikely for this specific XSS sink), it would be localized via wp_localize_script.
- Script Handle: Likely related to
wsm_admin_interfaceorwsm_stats. - Localized Variable: Based on
includes/wsm_admin_interface.php, the plugin often localizes variables. However, for a simple shortcode attribute reflection, a nonce is not required.
5. Exploitation Strategy
Step 1: Authentication
Authenticate as a user with at least Contributor permissions using the http_request tool to obtain session cookies.
Step 2: Inject Payload via Post Creation
Create a new post containing the malicious shortcode. Since Contributors can create posts but not publish them, we will save it as a "Pending Review" or a draft.
HTTP Request:
- Method: POST
- URL:
{{base_url}}/wp-admin/post-new.php(initial GET to get_wpnonce) - URL:
{{base_url}}/wp-admin/post.php(the POST to save) - Payload:
post_ID=[ID] &action=editpost &post_type=post &post_status=pending &post_title=Vulnerability Test &content=[wsm_showDayStatsGraph height='"><script>alert(document.cookie)</script>'] &_wpnonce=[NONCE]
Step 3: Trigger Execution
As an Administrator, view the "Pending" post. The XSS payload will execute.
HTTP Request:
- Method: GET
- URL:
{{base_url}}/?p=[POST_ID]&preview=true(or the direct URL if published)
6. Test Data Setup
- User: Ensure a user with the Contributor role exists (e.g., username
contributor_user, passwordpassword123). - Plugin: Ensure WP Visitor Statistics (Real Time Traffic) version 8.4 is installed and active.
- Permissions: No special plugin settings are required, as the shortcode is registered globally on
init.
7. Expected Results
- The Contributor successfully saves the post with the malicious
heightattribute. - When the Admin views the post, the HTML source code should contain:
... height=""><script>alert(document.cookie)</script>" ... - The browser will trigger an alert box displaying the session cookies.
8. Verification Steps
- Check Database: Use WP-CLI to verify the shortcode is stored in the
wp_poststable:wp post list --post_type=post --fields=ID,post_content --status=pending - Confirm Output: Use
http_requestto fetch the rendered post content and grep for the script:# Using the http_request tool response = http_request("GET", "{{base_url}}/?p=[POST_ID]") if "<script>alert(document.cookie)</script>" in response.body: print("Vulnerability Confirmed")
9. Alternative Approaches
If the height attribute is reflected inside a style attribute (e.g., <div style="height: [PAYLOAD]">), a different breakout might be required:
- Payload:
100px; background-image: url("javascript:alert(1)"); - Payload:
100px" onmouseover="alert(1)" data-foo="
If the plugin implements a check like if (!current_user_can('manage_options')) return; inside the shortcode handler (as seen in wsm_showDayStats), the Contributor won't see the payload, but the Admin will see it when reviewing the post, which still satisfies the conditions for a successful Stored XSS attack against an elevated user.
Summary
The WP Visitor Statistics (Real Time Traffic) plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) via the 'wsm_showDayStatsGraph' shortcode in versions up to 8.4. Authenticated attackers with Contributor-level access or higher can inject arbitrary JavaScript by supplying a malicious payload in the 'height' attribute of the shortcode, which is subsequently rendered without proper sanitization or escaping.
Vulnerable Code
// includes/wsm_statistics.php function wsm_showDayStatsGraph($atts, $content = "") { // ... (lines 140-160 approx) $atts = shortcode_atts(array( 'title' => esc_html(__('Today', 'wp-stats-manager')), 'height' => '400px', 'width' => '100%' ), $atts, WSM_PREFIX . '_showDayStatsGraph'); $height = $atts['height']; $width = $atts['width']; // ... $html = '<div id="' . WSM_PREFIX . 'DayStatsGraph" style="height:' . $height . '; width:' . $width . ';"></div>'; return $html; }
Security Fix
@@ -155,7 +155,7 @@ 'width' => '100%' ), $atts, WSM_PREFIX . '_showDayStatsGraph'); - $height = $atts['height']; + $height = esc_attr($atts['height']); $width = $atts['width']; $html = '<div id="' . WSM_PREFIX . 'DayStatsGraph" style="height:' . $height . '; width:' . $width . ';"></div>';
Exploit Outline
To exploit this vulnerability, an attacker must have at least Contributor-level permissions to create or edit a post. The attacker embeds the shortcode `[wsm_showDayStatsGraph height='"><script>alert(document.cookie)</script>']` into a post's content. When a user with elevated privileges (like an Administrator) views the post or previews it during review, the malicious script executes within their browser context because the 'height' attribute is reflected into the style attribute of a div tag without being escaped, allowing an attribute breakout.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.