RSS Aggregator <= 5.0.10 - Reflected Cross-Site Scripting via 'template' Parameter
Description
The RSS Aggregator plugin for WordPress is vulnerable to Reflected Cross-Site Scripting via the 'template' parameter in all versions up to, and including, 5.0.10 due to insufficient input sanitization and output escaping on user supplied attributes. 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.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=5.0.10What Changed in the Fix
Changes introduced in v5.0.11
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-1216 - WP RSS Aggregator Reflected XSS ## 1. Vulnerability Summary The **WP RSS Aggregator** plugin (versions <= 5.0.10) contains a reflected cross-site scripting (XSS) vulnerability in its AJAX rendering logic. Specifically, the `template` parameter within th…
Show full research plan
Exploitation Research Plan: CVE-2026-1216 - WP RSS Aggregator Reflected XSS
1. Vulnerability Summary
The WP RSS Aggregator plugin (versions <= 5.0.10) contains a reflected cross-site scripting (XSS) vulnerability in its AJAX rendering logic. Specifically, the template parameter within the JSON-encoded data POST variable is reflected into the page without sufficient sanitization or escaping when the plugin fails to find a corresponding display template. This allows unauthenticated attackers to execute arbitrary JavaScript in the context of a user's browser by tricking them into submitting a specially crafted POST request.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
wpra.render.display(andnopriv_wpra.render.display) - Vulnerable Parameter:
data(specifically thetemplatekey inside the JSON payload) - Authentication: None (Unauthenticated)
- Preconditions: The plugin must be active.
- Vulnerability Type: Reflected XSS (POST-based)
3. Code Flow
- Entry Point: In
core/modules/renderer.php, the plugin registers the AJAX actionwp_ajax_nopriv_wpra.render.displaywhich maps to the$ajaxRenderanonymous function. - Input Retrieval:
$ajaxRenderretrieves user input usingfilter_input(INPUT_POST, 'data')and decodes it as JSON into the$dataarray. - Validation Bypass: The code checks that
$data['id']and$data['page']are numeric. Other fields liketemplateare not validated at this stage. - Rendering Call: It calls
$renderer->renderArgs($data, 'shortcode'). - Vulnerable Processing: In
core/src/Renderer.php,renderArgscalls$this->parseArgs($args, $type). - Triggering the Sink:
parseArgsextracts$v4Slug = trim($args['template'] ?? '').- It then calls
$this->displays->getByV4Slug($v4Slug). - If
$v4Slugis a malicious payload (and thus doesn't exist as a valid slug),getByV4Slugreturns aResult::Errobject containing an error message (e.g.,"Display template '<payload>' not found"). renderArgsreceives this error and returns$this->adminMessage($result->error()->getMessage()).
- Execution: The
adminMessagemethod (likely a simple HTML wrapper) echoes the unsanitized error message containing the payload back to the browser with atext/htmlcontent type.
4. Nonce Acquisition Strategy
According to the source code in core/modules/renderer.php, the wpra.render.display AJAX handler does not perform any nonce verification.
$ajaxRender = function () use ( $renderer ) {
$dataJson = filter_input( INPUT_POST, 'data' );
$data = json_decode( $dataJson, true );
// ... validation of id and page only ...
echo $renderer->renderArgs( $data, 'shortcode' );
die();
};
add_action( 'wp_ajax_nopriv_wpra.render.display', $ajaxRender );
Because check_ajax_referer or wp_verify_nonce is missing, an attacker can trigger this endpoint unauthenticated without a nonce.
5. Exploitation Strategy
Since the reflection occurs via a POST request, the "link clicking" mentioned in the description refers to a CSRF-style attack where a victim is lured to an attacker-controlled page that auto-submits a POST form to the vulnerable WordPress site.
Steps for automated PoC:
- Target:
POST /wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Payload:
action:wpra.render.displaydata:{"id":1,"page":1,"template":"<script>alert('XSS_SUCCESS')</script>"}
- Tool: Use
http_requestto send the POST request.
6. Test Data Setup
No specific configuration is strictly required as the vulnerability is triggered on the error path (providing a template that does not exist).
- Ensure the plugin
wp-rss-aggregatoris installed and activated. - (Optional) Create one Feed Source via WP-CLI to ensure the system is initialized:
wp post create --post_type=wprss_feed --post_title="Test Feed" --post_status=publish
7. Expected Results
The server should respond with a 200 OK status and a body containing the unescaped script tag.
Example Response Body:
<div class="wpra-message wpra-error">Display with slug "<script>alert('XSS_SUCCESS')</script>" not found.</div>
(Note: If the message wrapper is different, the key is the presence of the raw script tag).
8. Verification Steps
- Execute the
http_requesttool with the POST parameters. - Inspect the
response_bodyfor the string<script>alert('XSS_SUCCESS')</script>. - Confirm that no HTML escaping (like
<) was applied to the script tag.
9. Alternative Approaches
If the error message reflection is patched or behaves differently, attempt to inject the payload into other keys in the data JSON object that might be reflected in the layout output:
alignparameter: AlthoughrenderDisplayusesesc_attr( $attributes['align'] ), verify if any other layouts (e.g., Pro templates if available) handle this differently.- Shortcode Context: Use a contributor-level account to create a post with `[wp-rss-aggregator template
Summary
The RSS Aggregator plugin for WordPress is vulnerable to reflected Cross-Site Scripting via the 'template' parameter in the 'wpra.render.display' AJAX action. Unauthenticated attackers can exploit this by sending a crafted POST request that results in the injection of arbitrary web scripts into the page when the plugin fails to find a corresponding display template and reflects the input unsanitized in an error message.
Vulnerable Code
// core/modules/renderer.php:43 $ajaxRender = function () use ( $renderer ) { $dataJson = filter_input( INPUT_POST, 'data' ); $data = json_decode( $dataJson, true ); if ( json_last_error() !== JSON_ERROR_NONE ) { status_header( 400 ); echo 'Could not decode JSON.'; die(); } $id = $data['id'] ?? null; $page = $data['page'] ?? null; if ( ! is_numeric( $id ) || ! is_numeric( $page ) ) { status_header( 400 ); echo 'Invalid ID or page number.'; die(); } echo $renderer->renderArgs( $data, 'shortcode' ); die(); }; add_action( 'wp_ajax_wpra.render.display', $ajaxRender ); add_action( 'wp_ajax_nopriv_wpra.render.display', $ajaxRender ); --- // core/src/Renderer.php:210 $v4Slug = trim( $args['template'] ?? '' ); $display = new Display( null ); if ( ! empty( $v4Slug ) ) { $result = $this->displays->getByV4Slug( $v4Slug ); if ( $result->isErr() ) { return $result; } $display = $result->get(); }
Security Fix
@@ -55,6 +55,12 @@ die(); } + $nonce = $data['_wpnonce'] ?? ''; + if ( ! wp_verify_nonce( $nonce, 'wpra_render_display' ) ) { + status_header( 403 ); + echo 'Nonce verification failed.'; + die(); + } // The $data array now contains all persisted shortcode attributes // from hx-vals, including id, page, sources, limit, exclude, pagination, template. // Pass the whole $data array to renderArgs. @@ -198,15 +198,15 @@ if ( 'block' === $type && ! empty( $id ) ) { $preserved_args = array( 'id' => $id, - 'align' => $args['align'] ?? null, - 'limit' => $args['limit'] ?? null, - 'pagination' => $args['pagination'] ?? null, + 'align' => isset( $args['align'] ) ? sanitize_text_field( $args['align'] ) : null, + 'limit' => isset( $args['limit'] ) ? sanitize_text_field( $args['limit'] ) : null, + 'pagination' => isset( $args['pagination'] ) ? sanitize_text_field( $args['pagination'] ) : null, ); // Filter out null values to keep $args clean - $args = array_filter($preserved_args, fn($value) => $value !== null); + $args = array_filter( $preserved_args, fn( $value ) => $value !== null ); } - $v4Slug = trim( $args['template'] ?? '' ); + $v4Slug = sanitize_text_field( $args['template'] ?? '' ); $display = new Display( null ); if ( ! empty( $v4Slug ) ) {
Exploit Outline
The exploit targets the AJAX endpoint `wp-admin/admin-ajax.php` using the action `wpra.render.display`. This endpoint is accessible to unauthenticated users (`nopriv`). An attacker creates a POST request where the `data` parameter contains a JSON-encoded object. By setting the `template` key in this object to an XSS payload (e.g., `<script>alert('XSS')</script>`), the plugin's `getByV4Slug` method fails to find a valid template with that name and returns an error object. The `renderArgs` method then retrieves the error message, which includes the raw, unescaped payload, and echoes it back to the browser. Because the endpoint does not require a nonce in vulnerable versions, an attacker can use a CSRF-style auto-submitting form on a malicious site to trick a logged-in user or even an unauthenticated visitor into triggering the script execution.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.