WordPress CVE-2026-64638 Pre-Auth XSS to RCE

thepill1 pts0 comments

XSS2Shell: WordPress Preauth XSS to RCE Chain (CVE-2026-64638) | PWN.AI Security ResearchBackXSS2Shell: WordPress Preauth XSS to RCE Chain (CVE-2026-64638)<br>PwnAI Research··10m10 min read·22 views

Pwn discovered a critical pre-auth XSS to RCE vulnerability chain affecting all versions of WordPress Core : the software that powers over 43% of all internet-facing websites. An estimated 500 million+ websites were vulnerable until today. We're calling it XSS2Shell . Check if you are affected here:

CVE-2026-64638 is exploitable entirely pre-authenticated (No account needed to exploit it). It lets a single failed login attempt run an attacker JavaScript execution in the WordPress origin, and against a logged-in administrator, towards full remote code execution on the server, reliably on all default Wordpress installs. All of our pwn.ai clients using our Asset Surface Management (ASM) product are protected from this vulnerability, and were notified as soon as pwn found it weeks early. If you are interested in trying out our ASM tool (Currently in beta), please sign up here: https://pwn.ai/asm

WordPress has confirmed the XSS to RCE chain and shipped an emergency patch in WordPress 7.0.3. The fix was backported to every maintained branch going back to WordPress 4.7. The vulnerability has been present since the earliest versions of WordPress and was somehow missed by every audit until now. It affects pretty much all WordPress sites that are still supported. If you run WordPress, update now.

This work is based off of a previous novel Same Origin Method Execution (SOME) exploitation technique that Paulos Yibelo published in pwn's blog and was nominated for Top Web Hacking Techniques of 2022: https://pwn.ai/blog/bypass-csp-using-wordpress-by-abusing-same-origin-method-execution , the research, when published led to CSP bypass affecting 43% of the internet.

Pwn was given this research as a starting ground and was asked to use it to create a full chain that is independent of the CSP bypass, and it took nearly 4 days of hard work to get there using open source models and its complex multi-agent workflow.

Watch the full exploit chain

Where it starts

When a user submits a username and password using wp-login.php, WordPress calls wp_signon(), which calls wp_authenticate(). If the username does not exist, wp_authenticate_username_password() in wp-includes/user.php builds an error:

return new WP_Error(<br>'invalid_username',<br>sprintf(<br>__( 'Error: The username %s is not registered on this site.' ),<br>$username<br>);

The submitted username is placed directly into HTML via sprintf. The variable $username at this point has been through sanitize_user() in non-strict mode, which calls wp_strip_all_tags().

So the question is: can anything survive wp_strip_all_tags() that later becomes dangerous?

The parser disagreement

wp_strip_all_tags() wraps around PHP's strip_tags(). Open the PHP documentation for strip_tags() and you will find that it identifies tags by looking for immediately followed by a letter. If there is whitespace between and the tag name, PHP does not recognize it as a tag.

strip_tags(''); // '' — survived<br>strip_tags(''); // '' — stripped

That is the entire bypass for the first parser.

Now trace where the surviving string goes. The error object travels back up through wp_signon() to wp-login.php, which passes it to login_header(), which passes it through wp_admin_notice(), which calls wp_kses_post().

KSES is WordPress's own HTML sanitization engine. It has a completely separate tokenizer. Pwn opened wp-includes/kses.php and looked at how it parses tag names. KSES handles the whitespace between and the tag name. To KSES, is a valid element.

And is in the KSES post allowlist. It is a standard HTML element used in image maps. The allowlist also includes and with a generous set of permitted attributes: id, class, href, name.

wp_strip_all_tags() says it is text. wp_kses_post() says it is allowed HTML. The browser receives live DOM elements that the attacker specified.

What to inject

Having DOM elements in the login page is not XSS by itself. There is no script execution yet. The next step is finding something on the page that will interact with the injected elements automatically.

Open wp-login.php and look at what scripts it enqueues. On the default login action (line 1516), WordPress enqueues user-profile. This is the script that manages the password generator, the password strength meter, and the admin color scheme picker. It was written for the profile editing page at /wp-admin/profile.php.

Why is it loaded on the login page? Because the login page also handles the password reset flow (action=resetpass), which needs the password generator. WordPress enqueues the script for the entire login page rather than conditionally for the reset action.

Now open wp-admin/js/user-profile.js and read the $(document).ready handlers.

Line 562 binds a delegated click handler on #color-picker for .color-option...

wordpress login chain username page execution

Related Articles