[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fI07QQr48Ghpf_b2WFKFc2OQgUesE6PD4DsOAYqFDgUY":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-25347","wp-rest-cache-unauthenticated-stored-cross-site-scripting","WP REST Cache \u003C= 2026.1.0 - Unauthenticated Stored Cross-Site Scripting","The WP REST Cache plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2026.1.0 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.","wp-rest-cache",null,"\u003C=2026.1.0","2026.1.1","high",7.2,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-03-23 00:00:00","2026-03-26 20:26:03",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F3a6242ab-eec1-4e3d-bd02-25381cd363b6?source=api-prod",4,[22,23,24,25,26,27,28],"admin\u002Fclass-admin.php","admin\u002Fincludes\u002Fclass-api-caches-table.php","admin\u002Fpartials\u002Fsub-cache-details.php","includes\u002Fcaching\u002Fclass-caching.php","includes\u002Fclass-plugin.php","readme.txt","wp-rest-cache.php","researched",false,3,"# Exploitation Research Plan: CVE-2026-25347\n\n## 1. Vulnerability Summary\nThe **WP REST Cache** plugin (up to version 2026.1.0) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)**. The plugin caches REST API requests and displays metadata about these caches (such as Request URI and Request Headers) in the WordPress admin dashboard. The vulnerability exists because the plugin fails to sanitize or escape this metadata when rendering the \"Caches\" list table and specific cache detail views.\n\nAn unauthenticated attacker can inject a malicious script into a REST API request (e.g., via query parameters). The plugin caches this request, storing the payload in the database. When an administrator views the plugin's settings or cache logs, the script executes in their browser.\n\n## 2. Attack Vector Analysis\n*   **Vulnerable Endpoint**: Any REST API endpoint cached by the plugin (default WordPress endpoints like `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts` are targeted by default).\n*   **Vulnerable Parameter**: The **Request URI** (specifically query strings) or **Request Headers**.\n*   **Authentication**: None (Unauthenticated).\n*   **Preconditions**: \n    1.  The plugin must be active.\n    2.  The targeted REST endpoint must be allowed for caching (the plugin allows default WP endpoints out of the box).\n    3.  A \"Must-Use\" plugin (`wp-rest-cache.php`) is typically required for the caching mechanism to trigger (automatically installed\u002Fcopied by the plugin).\n\n## 3. Code Flow\n1.  **Storage (Injection)**:\n    *   An unauthenticated user sends a GET request to a cached endpoint: `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?debug=\u003Cscript>alert(1)\u003C\u002Fscript>`.\n    *   The plugin (via the MU-plugin or early hooks) intercepts the request.\n    *   `WP_Rest_Cache_Plugin\\Includes\\Caching\\Caching::set_cache` is called.\n    *   The URI (including the XSS payload) is passed to `register_endpoint_cache`.\n    *   The data is stored in the `{prefix}wrc_caches` table. The `request_uri` column now contains the raw payload.\n\n2.  **Display (Sink)**:\n    *   An administrator navigates to **Settings > WP REST Cache**.\n    *   The page initializes the `WP_Rest_Cache_Plugin\\Admin\\Includes\\API_Caches_Table` class (derived from `WP_List_Table`).\n    *   In `admin\u002Fincludes\u002Fclass-api-caches-table.php`, the `column_default` method (line 198) is used to render columns like `request_uri`.\n    ```php\n    public function column_default( $item, $column_name ): string {\n        return $item[ $column_name ]; \u002F\u002F RAW OUTPUT SINK\n    }\n    ```\n    *   Additionally, the `column_cache_key` method (line 119) renders the `cache_key` without escaping the anchor text:\n    ```php\n    $title = sprintf(\n        '\u003Cstrong>\u003Ca href=\"?page=%s&sub=%s&cache_key=%s\">%s\u003C\u002Fa>\u003C\u002Fstrong>',\n        esc_attr( $page ),\n        'cache-details',\n        esc_attr( $item['cache_key'] ),\n        $item['cache_key'] \u002F\u002F RAW OUTPUT SINK\n    );\n    ```\n\n## 4. Nonce Acquisition Strategy\nThis exploit is **unauthenticated** and does not require a nonce for the injection phase. The payload is \"injected\" simply by making a standard GET request to the WordPress REST API. \n\nIf the automated agent needs to verify the XSS by performing an action (like clearing the cache) via the admin UI, it would find the nonce in the admin page HTML.\n*   **Admin Page**: `\u002Fwp-admin\u002Foptions-general.php?page=wp-rest-cache`\n*   **Nonce Localized Key**: The plugin does not appear to use `wp_localize_script` for these specific nonces in the provided source; they are generated inline in the table columns:\n    *   `wp_create_nonce( 'wp_rest_cache_flush_cache' )`\n    *   `wp_create_nonce( 'wp_rest_cache_delete_cache' )`\n\n**Note**: For the purpose of proving Stored XSS, no nonce is needed; the goal is to store the payload and observe its execution upon an admin's page load.\n\n## 5. Exploitation Strategy\nThe exploitation involves sending a request that the plugin will log.\n\n1.  **Injection Request**:\n    *   **Method**: `GET`\n    *   **URL**: `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?id=1&xss_param=\u003Cimg src=x onerror=alert(document.domain)>`\n    *   **Headers**: None required.\n    *   **Expected Behavior**: The REST API responds normally. The plugin detects the GET request to a cacheable endpoint and records the URI in the database.\n\n2.  **Triggering the XSS**:\n    *   Log into WordPress as an **Administrator**.\n    *   Navigate to `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Foptions-general.php?page=wp-rest-cache`.\n    *   The browser will render the `WP_List_Table`. The row for the injected request will contain the payload in the \"Request URI\" column, triggering the `alert`.\n\n## 6. Test Data Setup\n1.  **Plugin Activation**: Install and activate `wp-rest-cache` version 2026.1.0.\n2.  **MU-Plugin Verification**: Ensure `wp-content\u002Fmu-plugins\u002Fwp-rest-cache.php` exists (this is how the plugin hooks into early requests).\n3.  **Content**: Ensure at least one post exists so that `\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts` returns a 200 OK (though the plugin may cache 404s\u002Fempty sets depending on configuration).\n\n## 7. Expected Results\n*   The injection request should result in a new entry in the `wrc_caches` table.\n*   When the admin page loads, the HTML source for the table will contain the literal string `\u003Cimg src=x onerror=alert(document.domain)>` within a `\u003Ctd>` tag.\n*   The `alert(document.domain)` will fire in the admin's browser session.\n\n## 8. Verification Steps\n1.  **DB Check**: Use WP-CLI to verify the payload is stored:\n    ```bash\n    wp db query \"SELECT request_uri FROM wp_wrc_caches WHERE request_uri LIKE '%onerror%'\"\n    ```\n2.  **HTTP Check**: Use the `http_request` tool to fetch the admin page and verify the unescaped payload exists in the response body:\n    ```bash\n    # (Simplified representation)\n    GET \u002Fwp-admin\u002Foptions-general.php?page=wp-rest-cache\n    # Check for: \u003Ctd>\u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?id=1&xss_param=\u003Cimg src=x onerror=alert(document.domain)>\u003C\u002Ftd>\n    ```\n\n## 9. Alternative Approaches\nIf `request_uri` is filtered by the web server or WordPress core before the plugin stores it:\n1.  **Header Injection**: Inject via a header that the plugin is configured to cache (e.g., if a developer adds a custom header to `wp_rest_cache_global_cacheable_request_headers`). \n    *   Request: `GET \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts` with Header `X-Custom-Header: \u003Cscript>alert(1)\u003C\u002Fscript>`.\n2.  **Cache Key Injection**: The `cache_key` itself is rendered raw in `column_cache_key`. If an attacker can influence the generation of the cache key (often a hash, but sometimes includes components of the URI), this is a viable sink.\n3.  **Details Page**: Check the specific \"Cache Details\" page: `?page=wp-rest-cache&sub=cache-details&cache_key=[KEY]`. Although `sub-cache-details.php` uses `esc_html` in most places, any overlooked metadata column would result in XSS.","The WP REST Cache plugin for WordPress stores REST API request metadata, such as URIs and headers, in its database to facilitate cache management. Versions up to 2026.1.0 are vulnerable to unauthenticated stored XSS because the plugin fails to sanitize or escape these values when rendering the cache list table in the admin dashboard, allowing an attacker to execute arbitrary scripts in an administrator's browser session.","\u002F\u002F admin\u002Fincludes\u002Fclass-api-caches-table.php:120\n$title        = sprintf(\n    '\u003Cstrong>\u003Ca href=\"?page=%s&sub=%s&cache_key=%s\">%s\u003C\u002Fa>\u003C\u002Fstrong>',\n    esc_attr( $page ),\n    'cache-details',\n    esc_attr( $item['cache_key'] ),\n    $item['cache_key']\n);\n\n---\n\n\u002F\u002F admin\u002Fincludes\u002Fclass-api-caches-table.php:198\npublic function column_default( $item, $column_name ): string {\n    return $item[ $column_name ];\n}","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rest-cache\u002F2026.1.0\u002Fadmin\u002Fincludes\u002Fclass-api-caches-table.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rest-cache\u002F2026.1.1\u002Fadmin\u002Fincludes\u002Fclass-api-caches-table.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rest-cache\u002F2026.1.0\u002Fadmin\u002Fincludes\u002Fclass-api-caches-table.php\t2024-03-07 20:02:54.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rest-cache\u002F2026.1.1\u002Fadmin\u002Fincludes\u002Fclass-api-caches-table.php\t2026-01-28 07:48:12.000000000 +0000\n@@ -127,7 +127,7 @@\n \t\t\tesc_attr( $page ),\n \t\t\t'cache-details',\n \t\t\tesc_attr( $item['cache_key'] ),\n-\t\t\t$item['cache_key']\n+\t\t\tesc_html( $item['cache_key'] )\n \t\t);\n \n \t\t$actions                  = [];\n@@ -196,7 +196,7 @@\n \t * @return string The output for this column.\n \t *\u002F\n \tpublic function column_default( $item, $column_name ): string {\n-\t\treturn $item[ $column_name ];\n+\t\treturn esc_html( $item[ $column_name ] );\n \t}","The exploit is achieved by sending a standard GET request to any REST API endpoint that the plugin is configured to cache (such as default WordPress endpoints like \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts). The attacker appends a malicious XSS payload as a query parameter (e.g., \u002Fwp-json\u002Fwp\u002Fv2\u002Fposts?debug=\u003Cscript>alert(1)\u003C\u002Fscript>). Because the plugin intercepts and logs the full Request URI into the database, the payload is stored. When an administrator later views the plugin's 'Caches' table at \u002Fwp-admin\u002Foptions-general.php?page=wp-rest-cache, the unescaped URI is rendered in the 'Request URI' column, triggering the stored script. No authentication or nonces are required for the initial injection.","gemini-3-flash-preview","2026-04-17 23:29:22","2026-04-17 23:29:50",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","2026.1.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-rest-cache\u002Ftags\u002F2026.1.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-rest-cache.2026.1.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-rest-cache\u002Ftags\u002F2026.1.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-rest-cache.2026.1.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-rest-cache\u002Ftags"]