SlimStat Analytics <= 5.3.3 - Unauthenticated Stored Cross-Site Scripting via 'fh' Parameter
Description
The SlimStat Analytics plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `fh` (fingerprint) parameter in all versions up to, and including, 5.3.3. This is due to insufficient input sanitization and output escaping on the fingerprint value stored in the database. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever an administrator views the Real-time Access Log report.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=5.3.3What Changed in the Fix
Changes introduced in v5.3.4
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2025-15057 ## 1. Vulnerability Summary The **SlimStat Analytics** plugin (<= 5.3.3) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin fails to sanitize or escape the `fh` (fingerprint) parameter col…
Show full research plan
Exploitation Research Plan - CVE-2025-15057
1. Vulnerability Summary
The SlimStat Analytics plugin (<= 5.3.3) is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin fails to sanitize or escape the fh (fingerprint) parameter collected during visitor tracking requests. This malicious payload is stored in the database and subsequently executed in the browser of an administrator when they view the Real-time Access Log report.
2. Attack Vector Analysis
- Endpoint: WordPress REST API or
admin-ajax.phptracking endpoint.- REST Route:
/wp-json/wp-slimstat/v1/track(Primary in version 5.3.x). - AJAX Action:
slimstat_track(Fallback/Alternative).
- REST Route:
- Vulnerable Parameter:
fh(Fingerprint Hash). - Authentication: None required (Unauthenticated). Tracking is designed to capture data from all site visitors.
- Preconditions: The plugin must have tracking enabled (default state).
3. Code Flow
- Entry Point (Tracking): The plugin registers a REST route in
wp_slimstat::init()via\SlimStat\Providers\RESTService::run(). - Data Acquisition: When a tracking request is sent (e.g., via the browser-side script
wp-slimstat.js), the server-side logic (likely insrc/Providers/RESTService.phporwp_slimstat::slimtrack_ajax()) extracts parameters from the request. - Storage: The
fhvalue is extracted and passed to a database insertion method, likelywp_slimstat_db::insert(). The source code forwp_slimstat_dbis not fully provided, but based on the vulnerability description, this value is stored without sanitization. - Sink (Rendering): An administrator accesses the dashboard at
/wp-admin/admin.php?page=slimview1. - Execution: The file
admin/view/right-now.phpcallswp_slimstat_db::get_recent(). It iterates through the results and renders the visit details. The fingerprint (fh) is typically displayed in a tooltip or a specific column (e.g., as a unique identifier for the visitor). Becauseadmin/view/right-now.phpfails to useesc_html()oresc_attr()on this value, the stored script executes.
4. Nonce Acquisition Strategy
Tracking requests in SlimStat usually require a nonce to prevent spam, even for unauthenticated users. This nonce is typically localized in the frontend.
- Identify Script Localization: The plugin enqueues the tracker in
wp_slimstat::enqueue_tracker. - Shortcode: The tracking script is usually loaded on any page where tracking is active. By default, this is the entire frontend.
- Extraction:
- Action: Create a dummy post/page to ensure the script triggers.
- Command:
wp post create --post_type=page --post_status=publish --post_title="Tracking Test" --post_content="Tracking Page" - Navigation: Use
browser_navigateto the URL of the new page. - JS Variable: SlimStat localizes its configuration in a variable named
SlimStatParams(inferred from common plugin patterns) or similar. - Verification: Use
browser_eval("window.SlimStatParams")to find the exact key. Based on typical SlimStat versions, the key isextensions.nonceor simplynonce.
5. Exploitation Strategy
The goal is to send a malicious fh value that stores a script to exfiltrate the administrator's cookies or create a new admin user.
Step-by-Step Plan:
- Identify Tracking Endpoint:
Verify if the site uses the REST API (preferred in 5.3.x) or AJAX. - Extract Nonce:
Navigate to the homepage and usebrowser_evalto extract the nonce from theSlimStatParamsobject.
Example:browser_eval("SlimStatParams.nonce"). - Send Malicious Tracking Request:
Perform a POST request to the REST API endpoint.- URL:
/wp-json/wp-slimstat/v1/track - Method: POST
- Content-Type:
application/x-www-form-urlencodedorapplication/json. - Parameters:
fh:<script>alert('CVE-2025-15057_XSS')</script>ref:http://example.com/(Base64 encoded if required by the plugin:aHR0cDovL2V4YW1wbGUuY29tLw==)res:1920x1080_wpnonce:[EXTRACTED_NONCE]
- URL:
Example HTTP Request (via http_request):
{
"method": "POST",
"url": "http://localhost:8080/wp-json/wp-slimstat/v1/track",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": "fh=%3Cscript%3Ealert(document.domain)%3C/script%3E&_wpnonce=[NONCE_VALUE]&url=http%3A%2F%2Flocalhost%3A8080%2F"
}
6. Test Data Setup
- Plugin Status: Ensure
wp-slimstatis active and version <= 5.3.3. - Tracking Settings: Ensure "Enable Tracking" is 'on' (default).
- Administrator User: Have an active admin user (standard in PoC environments).
- Target Page: No specific shortcode is needed as SlimStat typically tracks all pages, but creating a public page ensures the tracker script and nonce are available.
7. Expected Results
- The tracking request returns a
200 OKor204 No Contentresponse. - The database (table
wp_slim_stats) now contains a record where thefingerprintcolumn holds the<script>payload. - When an admin navigates to
/wp-admin/admin.php?page=slimview1, a JavaScript alert box appearing with the site's domain.
8. Verification Steps
- Database Check:
Use WP-CLI to verify the payload is stored:wp db query "SELECT fingerprint FROM wp_slim_stats ORDER BY st_id DESC LIMIT 1;" - Admin UI Check:
Usebrowser_navigateas an administrator tohttp://localhost:8080/wp-admin/admin.php?page=slimview1.
Check for the presence of the alert or inspect the page source for the unescaped payload.
9. Alternative Approaches
- Encoding: If the payload is blocked by simple string matching, try encoding the
fhparameter or using an<img>tag withonerror. - AJAX Fallback: If the REST API is disabled, use the
admin-ajax.phpendpoint:- Action:
slimstat_track - Body:
action=slimstat_track&fh=<script>...</script>&_wpnonce=[NONCE]
- Action:
- Referer/URL Parameters: Some versions of SlimStat also exhibit XSS in the
ref(referer) orurlparameters if they are not correctly handled in the "Recent Referers" or "Top Pages" reports. These can be tested iffhfails.
Summary
The SlimStat Analytics plugin for WordPress is vulnerable to unauthenticated stored Cross-Site Scripting via the 'fh' (fingerprint) parameter. Malicious scripts injected into this parameter during tracking requests are stored in the database and executed when an administrator views the Real-time Access Log report.
Vulnerable Code
// wp-slimstat.php (Line 144) - Tracking data intake public static function slimtrack_ajax() { // ... self::$data_js = apply_filters('slimstat_filter_pageview_data_js', self::$raw_post_array); $site_host = parse_url(get_site_url(), PHP_URL_HOST); self::$stat['referer'] = ''; if (!empty(self::$data_js['ref'])) { self::$stat['referer'] = self::_base64_url_decode(self::$data_js['ref']); --- // admin/view/right-now.php (Line 40) - Report data retrieval and loop wp_slimstat_db::$debug_message = ''; $all_results = wp_slimstat_db::get_recent(wp_slimstat_reports::$reports['slim_p7_02']['callback_args']); // ... // Loop through the results for display for ($i = 0; $i < $count_page_results; $i++) { $date_time = "<i class='spaced slimstat-font-clock slimstat-tooltip-trigger' title='" . __('Date and Time', 'wp-slimstat') . "'></i> " . date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $results[$i]['dt'], true); // ... (rendering logic follows)
Security Fix
@@ -205,7 +205,7 @@ } $results[$i]['resource'] = rawurldecode($results[$i]['resource']); - $results[$i]['resource'] = "<a class='slimstat-font-logout slimstat-tooltip-trigger' target='_blank' title='" . htmlentities(__('Open this URL in a new window', 'wp-slimstat'), ENT_QUOTES, 'UTF-8') . "' href='" . htmlentities($results[$i]['resource'], ENT_QUOTES, 'UTF-8') . "'></a> <a class='slimstat-filter-link' href='" . wp_slimstat_reports::fs_url('resource equals ' . esc_url($results[$i]['resource'])) . "'>" . esc_html($resource_title) . '</a>'; + $results[$i]['resource'] = "<a class='slimstat-font-logout slimstat-tooltip-trigger' target='_blank' title='" . esc_attr(__('Open this URL in a new window', 'wp-slimstat')) . "' href='" . esc_url($results[$i]['resource']) . "'></a> <a class='slimstat-filter-link' href='" . wp_slimstat_reports::fs_url('resource equals ' . $results[$i]['resource']) . "'>" . esc_html($resource_title) . '</a>';
Exploit Outline
The exploit is achieved by sending a malicious tracking request as an unauthenticated visitor. 1. First, an attacker retrieves the tracking nonce by visiting the site's frontend and extracting the `extensions.nonce` value from the global `SlimStatParams` JavaScript object. 2. The attacker then constructs a POST request to the REST API tracking endpoint (`/wp-json/wp-slimstat/v1/track`) or the AJAX endpoint (`admin-ajax.php?action=slimstat_track`). 3. The payload is placed in the `fh` (fingerprint) parameter, for example: `fh=<script>alert(document.domain)</script>`. 4. When an administrator later logs in and navigates to the 'Access Log' or 'Real-time' report page within the WordPress dashboard, the stored script executes in their browser session, potentially allowing for cookie theft or administrative account takeover.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.