WordPress 7.0.3 fixes a pre-auth login-page XSS that can turn administrator interaction with a malicious site into PHP execution.2026-08-07T19:25:00.000Z5 min2026appsecthreats
5 min read
Read format

Failed WordPress Login Input Reaches Conditional Server Code Execution

WordPress 7.0.3 fixes a pre-auth login-page XSS that can turn administrator interaction with a malicious site into PHP execution.

By Justin Howe
A cream website login panel split by a narrow malformed gap that exposes a burgundy-lit server cavity behind it.

WordPress 7.0.3 fixes CVE-2026-64638, the XSS2Shell chain built from a reflected cross-site scripting flaw on the public login screen. WordPress describes it as “pre-auth reflected cross-site scripting (XSS) on the login screen with potential to lead to PHP code execution.” Every WordPress version before a fixed release is affected. Pwn.ai says WordPress powers more than 43 percent of internet-facing websites and estimated that more than 500 million websites were vulnerable before the fix. WordPress says the full jump to PHP execution requires a logged-in administrator to visit and explicitly interact with a malicious third-party site.

A failed login can run attacker-controlled JavaScript in the site’s origin. No account is required. Server compromise depends on later administrator activity.

XSS2Shell joins two parser gaps

Pwn.ai researcher Nigusu Kasahun traces the first break to the username in a failed wp-login.php request. WordPress passes that value through sanitize_user() and wp_strip_all_tags(), which relies on PHP’s strip_tags(). Because PHP does not recognize a tag when whitespace follows the opening angle bracket, a value beginning with a form such as < area survives as text.

The error later passes through WordPress’s KSES sanitizer. Its different tokenizer accepts the whitespace and interprets the same bytes as live HTML. Permitted elements such as area, div, and button carry attributes into the next stage.

The page now contains attacker-chosen DOM elements. WordPress must still interact with them.

WordPress loads user-profile.js on the login page because the same page handles password resets. The script searches for profile-editor controls that normally do not exist there.

Injected elements imitate the expected selectors. The script auto-clicks one, reaches a delegated handler, and calls jQuery.post() with the undefined global ajaxurl.

The injected area element receives the ID ajaxurl, which browser named-property behavior exposes as window.ajaxurl. Converting that element to a string gives jQuery its attacker-controlled href value.

The destination is WordPress’s public REST API. Method override changes the POST into a GET, and JSONP wraps the response as a callback. Because the response carries a JavaScript content type, jQuery evaluates it as script within the WordPress origin. One failed login now produces attacker-selected JavaScript in the WordPress origin.

A four-stage flow from a crafted failed login through parser disagreement and browser execution to conditional PHP execution that requires administrator interaction.

Figure details

A crafted username in a failed login survives PHP tag stripping because a space follows the opening angle bracket. WordPress KSES reparses the bytes as allowed HTML. Injected DOM elements match selectors in user-profile.js, auto-trigger a click, and replace the undefined ajaxurl through browser named properties. A REST JSONP response is then evaluated in the WordPress origin. Reaching PHP execution is a separate, conditional stage that requires a logged-in administrator to visit and interact with an attacker-controlled page, allowing the attacker to approve an Application Password and upload a plugin.

Administrator interaction reaches server code

The published chain uses a malicious page with a child window. It moves the administrator’s main window to WordPress’s Application Password approval screen, then fires the login-page XSS in the child. The callback crosses the opener relationship and activates the approval control inside the authenticated session.

That produces an Application Password. When the account has unfiltered_html, the attacker uses the REST API to publish JavaScript. The administrator’s browser is sent there, where the script reads a plugin-upload nonce and submits a ZIP containing PHP.

A PHP file extracted below wp-content/plugins/ can be directly reachable without activation, and the researcher demonstrated that chain on default installations before removing the created password, page, and plugin.

None of the cited primary records reports active exploitation, identifies an actor, or publishes campaign indicators. The public disclosure includes a pre-auth payload and enough mechanism detail to make a vulnerable, reachable login page actionable.

Verify the release and history

WordPress recommends updating immediately. The exact patched releases are 7.0.3, 6.9.6, 6.8.7, 6.7.6, 6.6.6, 6.5.9, 6.4.9, 6.3.9, 6.2.10, 6.1.11, 6.0.13, 5.9.14, 5.8.14, 5.7.16, 5.6.18, 5.5.19, 5.4.20, 5.3.22, 5.2.25, 5.1.23, 5.0.26, 4.9.30, 4.8.29, and 4.7.34. WordPress states that only the newest version is actively supported. Administrators on an older branch should verify its exact fixed backport from the running instance.

Inventory production, staging, disaster-recovery, and forgotten marketing instances. Record each core version and whether wp-login.php is reachable through an alternate hostname. Managed hosting does not replace version readback.

Temporary filtering can reduce exposure while an update is tested. The disclosed variants use more than one REST path, so a single-query WAF rule does not fix the core parser disagreement.

The browser path leaves distinct evidence.

The failed login supplies the browser primitive. Server compromise requires a logged-in administrator to cross the later interaction path. For valuable sites exposed after disclosure, reconstruct Application Password approval, new posts, plugin uploads, PHP changes, administrator browsing, and related endpoint activity. The primary records provide no campaign indicators, so this is mechanism-derived reconstruction.

Kasahun’s disclosed values make that reconstruction specific. Search for /wp-admin/profile.php, wp-admin/js/user-profile.js, #color-picker, .color-option, .reset-pass-submit, .wp-generate-pw, and ajaxurl. Preserve requests containing /?rest_route=/&_method=GET&_jsonp=alert&_envelope=1, the callback window.opener.approve.click, and visits to /wp-admin/authorize-application.php. These values describe the published chain and are not campaign-specific indicators.

Prove site and administrator path

Inventory every production, staging, recovery, and forgotten site. Verify remediation from the running instance or deployment artifact: WordPress 7.0.3 or later, or an exact fixed backport. An update setting or WAF rule is not enough.

For valuable sites exposed after disclosure, preserve proxy, web server, PHP, WordPress audit, administrator endpoint, and file-integrity evidence before cleanup. Account for failed-login input, administrator sessions, Application Passwords, posts, plugin uploads, PHP changes, and outbound traffic. Revoke unexplained Application Passwords and rotate administrator secrets from a clean system.

Separate staged artifacts from execution. An approved Application Password, an uploaded plugin ZIP, or an extracted PHP file establishes that a stage of the chain reached the server. Those artifacts alone do not establish that the PHP file ran. Require a request to the extracted path and supporting web, PHP, process, response, or network evidence before recording server-side execution.

The expected result is a listed fixed release and no successful administrator interaction with the published XSS2Shell path during the exposure window.

The blind spot is missing history. A site can be fully patched and still lack the records needed to say whether an administrator carried the exploit across the line. For a business-critical WordPress instance, that uncertainty should change the recovery choice: trusted code and content are stronger than a clean finding made from silence.

Primary sources

Continue reading

Article figurePinch or double-tap to zoom, then drag to pan.