Visitor Traffic Real Time Statistics <= 8.4 - Unauthenticated Stored Cross-Site Scripting
Description
The Visitor Traffic Real Time Statistics plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'page_title' parameter in all versions up to, and including, 8.4 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever an admin user accesses the Traffic by Title section.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/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# Exploitation Research Plan - CVE-2026-2936 ## 1. Vulnerability Summary The **Visitor Traffic Real Time Statistics** plugin (<= 8.4) contains a stored Cross-Site Scripting (XSS) vulnerability via the `page_title` parameter. The vulnerability exists because the plugin's visitor tracking logic fails…
Show full research plan
Exploitation Research Plan - CVE-2026-2936
1. Vulnerability Summary
The Visitor Traffic Real Time Statistics plugin (<= 8.4) contains a stored Cross-Site Scripting (XSS) vulnerability via the page_title parameter. The vulnerability exists because the plugin's visitor tracking logic fails to sanitize the page title before storing it in the database, and the admin dashboard subsequently renders this title using the dangerous innerHTML property in its JavaScript-based data tables.
An unauthenticated attacker can send a crafted AJAX request to the tracking endpoint to inject a malicious script. When an administrator views the Traffic by Title section in the plugin's dashboard, the script executes in their browser context.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action:
ahcfree_track_visitor(Unauthenticated) - Vulnerable Parameter:
page_title - Preconditions: The plugin must be active.
- Authentication: No authentication is required for the tracking endpoint (
wp_ajax_nopriv_ahcfree_track_visitor).
3. Code Flow
- Entry Point: The attacker sends a POST request to
admin-ajax.phpwithaction=ahcfree_track_visitor. - Tracking Logic: The AJAX handler (inferred to be in
init.phporVisitors-Traffic-Real-Time-Statistics.php) instantiates theWPHitsCounterclass. - Source (
WPHitsCounter.php):- The constructor
__construct($page_id, $page_title = NULL, $post_type = NULL)receives the$page_titleparameter directly from the AJAX handler. - At line 63:
$this->pageTitle = $page_title;— The input is assigned to the class property without any sanitization (unlikeuserAgentwhich usesahc_free_sanitize_text_or_array_field).
- The constructor
- Storage:
traceVisitorHit()calls$this->updateTitleTraffic($this->pageId, $this->pageTitle)(line 156).- This function stores the raw payload in the
ahc_title_traffictable (inferred database table name).
- Sink (
js/ahcfree_js_scripts.js):- The admin views the "Traffic by Title" page (
admin.php?page=ahc_hits_counter_menu_free). - The DataTable
traffic_by_title(line 141) fetches data via AJAX fromaction=traffic_by_title. - In the
renderfunction for the hits column (lines 160-184):if (row.til_page_title) { var tempDiv = document.createElement('div'); tempDiv.innerHTML = row.til_page_title; // DANGEROUS SINK: Executes injected HTML pageTitle = tempDiv.textContent || tempDiv.innerText || 'Unknown'; } - Even if
tempDivis never appended to the DOM, settinginnerHTMLwith a payload like<img src=x onerror=alert(1)>triggers immediate execution.
- The admin views the "Traffic by Title" page (
4. Nonce Acquisition Strategy
The ahcfree_track_visitor action is used for frontend visitor tracking and does not require a nonce.
- Evidence: In
Visitors-Traffic-Real-Time-Statistics.php(lines 80-97), the functionahcfree_send_first_visit_request()generates a script that sends the tracking request. Thexhttp.sendpayload (line 93) contains onlyaction,page_id,page_title,post_type,referer,useragent,servername,hostname, andrequest_uri. No nonce is included.
5. Exploitation Strategy
- Payload: Use an
<img>tag with anonerrorevent to trigger the XSS.page_title:<img src=x onerror="alert('CVE-2026-2936_XSS')">
- Request: Perform an unauthenticated POST request to the AJAX endpoint.
- Trigger: Log in as an administrator and navigate to the plugin's dashboard.
HTTP Request Details
- Method:
POST - URL:
http://<target>/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded
- Body Parameters:
action:ahcfree_track_visitorpage_id:9999(A unique ID ensures the hit is recorded)page_title:<img src=x onerror="alert('CVE-2026-2936_XSS')">post_type:page(Must bepost,product, orpageto satisfy the check intraceVisitorHit)request_uri:tracking-test
6. Test Data Setup
- Install and activate the plugin "Visitor Traffic Real Time Statistics" version 8.4.
- No specific pages or shortcodes are required since the
ahcfree_track_visitorendpoint accepts arbitrarypage_idandpage_titlevalues from the request.
7. Expected Results
- The tracking request should return a successful response (likely
0or empty if standard WordPress AJAX response, or a specific success message). - The payload will be stored in the database.
- When the admin navigates to
wp-admin/admin.php?page=ahc_hits_counter_menu_free, the browser will execute the alert.
8. Verification Steps
- Check Database: Use WP-CLI to verify the payload is stored.
wp db query "SELECT til_page_title FROM wp_ahc_title_traffic WHERE til_page_id = 9999" - Simulate Admin View: Use the
browser_navigatetool to log in as admin and visit the dashboard page. - Observe Execution: Confirm the
alertis triggered via Playwright's dialog handling.
9. Alternative Approaches
If the DataTable render function is patched, look for other sinks in the admin dashboard:
- The dashboard might display the "Top Pages" or "Traffic by Title" in a different widget or export function.
- In
js/ahcfree_js_scripts.js(line 176), the title is also used in adata-page-titleattribute:
If thedata-page-title="${pageTitle.replace(/"/g, '"')}"pageTitleextraction failed to strip the payload, it might be injectable here if the quotes are not handled perfectly, though theinnerHTMLsink is the primary target.
Summary
The Visitor Traffic Real Time Statistics plugin is vulnerable to unauthenticated stored Cross-Site Scripting via the 'page_title' parameter. Attackers can inject malicious scripts into the tracking database by sending a crafted AJAX request, which then executes when an administrator views the Traffic by Title section in the dashboard.
Vulnerable Code
// WPHitsCounter.php (~line 63) public function __construct($page_id, $page_title = NULL, $post_type = NULL) { // ... (truncated) $this->pageTitle = $page_title; // ... (truncated) } --- // js/ahcfree_js_scripts.js (~line 160) if (row.til_page_title) { var tempDiv = document.createElement('div'); tempDiv.innerHTML = row.til_page_title; // DANGEROUS SINK pageTitle = tempDiv.textContent || tempDiv.innerText || 'Unknown'; } --- // functions.php (~line 3292 in 8.4) $page_id = intval($_POST['page_id']); $page_title = ahc_free_sanitize_text_or_array_field($_POST['page_title']);
Security Fix
@@ -3289,7 +3301,11 @@ $page_id = intval($_POST['page_id']); - $page_title = ahc_free_sanitize_text_or_array_field($_POST['page_title']); + // $page_title = ahc_free_sanitize_text_or_array_field($_POST['page_title']); + + $page_title_raw = isset($_POST['page_title']) ? wp_unslash($_POST['page_title']) : ''; + $page_title_decoded = html_entity_decode($page_title_raw, ENT_QUOTES | ENT_HTML5, 'UTF-8'); + $page_title = sanitize_text_field(wp_strip_all_tags($page_title_decoded, true)); $post_type = ahc_free_sanitize_text_or_array_field($_POST['post_type']); $_SERVER['HTTP_REFERER'] = ahc_free_sanitize_text_or_array_field($_POST['referer']); $_SERVER['HTTP_USER_AGENT'] = ahc_free_sanitize_text_or_array_field($_POST['useragent']); @@ -404,7 +404,7 @@ } </style> <div class="traffic-header-enhanced"> - <div class="page-title">${pageTitle}</div> + <div class="page-title"></div> <div class="hits">${hits.toLocaleString()} hits</div> </div> <div class="traffic-image-container"> @@ -416,6 +416,8 @@ // Update modal content and title jQuery('#TrafficStatsModal .modal-body').html(headerHtml); + jQuery('#TrafficStatsModal .modal-body').html(headerHtml); + jQuery('#TrafficStatsModal .modal-body .page-title').text(pageTitle); jQuery('#TrafficStatsModal .modal-title').text('Page Statistics: ' + pageTitle);
Exploit Outline
The exploit targets the unauthenticated AJAX tracking endpoint `ahcfree_track_visitor`. An attacker sends a POST request to `wp-admin/admin-ajax.php` with the `action` parameter set to `ahcfree_track_visitor`. The payload is placed in the `page_title` parameter (e.g., `<img src=x onerror=alert(1)>`). Because the plugin fails to adequately sanitize this input before storing it in the database and subsequently uses `innerHTML` to render the data in the admin's 'Traffic by Title' DataTable, the script executes in the context of any administrator viewing the statistics dashboard.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.