CVE-2025-15057

SlimStat Analytics <= 5.3.3 - Unauthenticated Stored Cross-Site Scripting via 'fh' Parameter

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
5.3.4
Patched in
1d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=5.3.3
PublishedJanuary 8, 2026
Last updatedJanuary 9, 2026
Affected pluginwp-slimstat

What Changed in the Fix

Changes introduced in v5.3.4

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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.php tracking endpoint.
    • REST Route: /wp-json/wp-slimstat/v1/track (Primary in version 5.3.x).
    • AJAX Action: slimstat_track (Fallback/Alternative).
  • 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

  1. Entry Point (Tracking): The plugin registers a REST route in wp_slimstat::init() via \SlimStat\Providers\RESTService::run().
  2. Data Acquisition: When a tracking request is sent (e.g., via the browser-side script wp-slimstat.js), the server-side logic (likely in src/Providers/RESTService.php or wp_slimstat::slimtrack_ajax()) extracts parameters from the request.
  3. Storage: The fh value is extracted and passed to a database insertion method, likely wp_slimstat_db::insert(). The source code for wp_slimstat_db is not fully provided, but based on the vulnerability description, this value is stored without sanitization.
  4. Sink (Rendering): An administrator accesses the dashboard at /wp-admin/admin.php?page=slimview1.
  5. Execution: The file admin/view/right-now.php calls wp_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). Because admin/view/right-now.php fails to use esc_html() or esc_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.

  1. Identify Script Localization: The plugin enqueues the tracker in wp_slimstat::enqueue_tracker.
  2. Shortcode: The tracking script is usually loaded on any page where tracking is active. By default, this is the entire frontend.
  3. 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_navigate to 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 is extensions.nonce or simply nonce.

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:

  1. Identify Tracking Endpoint:
    Verify if the site uses the REST API (preferred in 5.3.x) or AJAX.
  2. Extract Nonce:
    Navigate to the homepage and use browser_eval to extract the nonce from the SlimStatParams object.
    Example: browser_eval("SlimStatParams.nonce").
  3. 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-urlencoded or application/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]

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

  1. Plugin Status: Ensure wp-slimstat is active and version <= 5.3.3.
  2. Tracking Settings: Ensure "Enable Tracking" is 'on' (default).
  3. Administrator User: Have an active admin user (standard in PoC environments).
  4. 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

  1. The tracking request returns a 200 OK or 204 No Content response.
  2. The database (table wp_slim_stats) now contains a record where the fingerprint column holds the <script> payload.
  3. When an admin navigates to /wp-admin/admin.php?page=slimview1, a JavaScript alert box appearing with the site's domain.

8. Verification Steps

  1. 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;"
  2. Admin UI Check:
    Use browser_navigate as an administrator to http://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 fh parameter or using an <img> tag with onerror.
  • AJAX Fallback: If the REST API is disabled, use the admin-ajax.php endpoint:
    • Action: slimstat_track
    • Body: action=slimstat_track&fh=<script>...</script>&_wpnonce=[NONCE]
  • Referer/URL Parameters: Some versions of SlimStat also exhibit XSS in the ref (referer) or url parameters if they are not correctly handled in the "Recent Referers" or "Top Pages" reports. These can be tested if fh fails.
Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-slimstat/5.3.3/admin/view/right-now.php /home/deploy/wp-safety.org/data/plugin-versions/wp-slimstat/5.3.4/admin/view/right-now.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-slimstat/5.3.3/admin/view/right-now.php	2025-12-17 11:24:04.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-slimstat/5.3.4/admin/view/right-now.php	2025-12-28 06:28:40.000000000 +0000
@@ -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.