A critical vulnerability chain, dubbed "XSS2Shell" by researchers at Pwn, has been discovered in WordPress that could allow an unauthenticated attacker to achieve full server takeover. The exploit chain begins with a seemingly minor cross-site scripting (XSS) flaw on the login page and culminates in remote code execution (RCE) on the web server.
The initial vulnerability resides in how WordPress processes error messages for invalid usernames during login attempts. When a user submits a non-existent username, WordPress echoes this input back in an error message. A key flaw exists in the `strip_tags()` function, a PHP built-in used by WordPress to sanitize the input. This function fails to recognize HTML tags if there is a space immediately following the opening bracket (e.g., `< area id=ajaxurl>`). Consequently, an attacker can inject a payload like `area id=ajaxurl` which bypasses `strip_tags()` unscathed.
However, a separate WordPress sanitizer, `wp_kses_post()`, interprets this malformed tag as a legitimate HTML element, rendering it in the browser's Document Object Model (DOM). This injected `area` element, with its `id` attribute set to "ajaxurl," then becomes accessible as a property on the browser's `window` object due to a standard HTML specification that exposes elements with `id` attributes as named properties of the `window` object.
The next step in the attack chain leverages a script intended for the profile page, `user-profile.js`, which is inadvertently loaded on the login page to handle password resets. This script monitors for a password-reset button and automatically triggers a click event. The injected DOM element, acting as a spurious password-reset button, causes the script to attempt to resolve an undefined JavaScript variable, `ajaxurl`. Due to the DOM clobbering effect, the injected `area` element with `id="ajaxurl"` is returned as the value for `window.ajaxurl`. This allows the attacker to control how the script handles subsequent network requests.
The compromised `ajaxurl` then directs a request to WordPress's REST API, tricking it into responding as JSONP. The attacker can manipulate the callback function name in the JSONP response, which, critically, accepts dots in addition to letters. This allows for traversing property chains across browser windows. The researchers adapted a 2022 technique to turn this into a cross-window click event, executing within an actual logged-in administrator's session rather than the attacker's own.
Achieving full server compromise requires a precise sequence of actions: opening a hidden browser window, navigating the administrator's browser to WordPress's application-password approval screen, and then triggering the cross-window click on the "approve" button. This action, executed with the administrator's genuine session cookies and nonces, grants the attacker a valid Application Password for the administrator account. WordPress's REST API accepts these Application Passwords via HTTP Basic authentication from any origin.
With an Application Password, the attacker's page can make authenticated cross-origin API calls. Since single-site WordPress administrators possess the `unfiltered_html` capability by default, script tags submitted by the attacker survive into published pages. This allows the attacker to publish a page containing their own JavaScript, which can then be used to upload and execute a plugin containing arbitrary PHP code.
The researchers demonstrated a proof-of-concept that ended with a minimal PHP file confirming code execution as the web server user, followed by a complete cleanup of all traces, including the revocation of the application password, deletion of the published page, and removal of the plugin directory. This demonstrated that the attack chain is pre-authentication, requires no user interaction beyond visiting a malicious link, and reliably leads to remote code execution on a default WordPress installation.
WordPress was notified of the vulnerability on July 27 and promptly released version 7.0.3 on August 6 to address the issue. The fix has been backported to all still-maintained WordPress branches, extending coverage back to WordPress 4.7. Users are strongly urged to update their WordPress installations immediately to mitigate this critical threat.






