WP2Shell: The Pre-Auth RCE Hiding in the Gap Between Two WordPress CVEs
WordPress shipped an emergency fix for two CVEs: a 7.5 route-confusion bug and a 9.1 SQL injection. Apart, both look manageable. Chained, they're a pre-auth RCE on a default install, no login needed. WP2Shell shows why one-CVE-at-a-time triage misses the ones that matter most.
TL;DR
On 17 July 2026, WordPress shipped an emergency release fixing two vulnerabilities: CVE-2026-60137 (a SQL injection scored 9.1) and CVE-2026-63030 (a REST API route-confusion issue scored 7.5). Both are serious findings on their own. But neither one, read from its score or its description alone, announces itself as unauthenticated remote code execution; the SQLi is flagged as "facilitated," and the route-confusion bug scores as a confidentiality-only issue. Chained together, they become a pre-auth RCE against a default WordPress install: no login, no plugins, no user interaction. WordPress runs a very large share of the public web, so the exposed population is measured in the tens of millions.
The two CVEs have different severities and different affected version ranges. That mismatch is a textbook example of why severity-ranked, one-CVE-at-a-time triage quietly fails on the vulnerabilities that matter most.
Patch to 6.8.6, 6.9.5, 7.0.2, or 7.1-beta2 now, then read on for why this one is worth understanding rather than just closing.
What happened
The chain was disclosed by Adam Kues of Searchlight Cyber's Assetnote research team, who reported it responsibly through WordPress's HackerOne programme and nicknamed it "WP2Shell." The companion SQL injection was reported separately by researchers TF1T, dtro, and haongo.
WordPress considered it serious enough to force automatic core updates. Partial proof-of-concept code is already circulating publicly, and early (unconfirmed at time of writing) reports of in-the-wild probing have started to appear. At time of writing, neither CVE is on the CISA KEV catalogue. Given the popularity of Wordpress and the attention this vulnerability chain is receiving, it’s expected successful exploitation will be observed in the near future.
The two bugs
CVE-2026-60137: SQL injection in WP_Query. WordPress fails to properly sanitise the author__not_in parameter of WP_Query, the core class behind almost every database query the CMS runs. When that parameter arrives as a string rather than an array, the sanitisation guard is skipped and the raw value is interpolated straight into a NOT IN clause. It's rated CVSS 9.1 (Critical) and reaches all the way back to WordPress 6.8.
On its own, though, this is a facilitated injection which needs something to feed untrusted input into that parameter. In practice that means an authenticated user or a plugin/theme that exposes it.
CVE-2026-63030: REST API batch route confusion. WordPress's batch endpoint at /wp-json/batch/v1 bundles several sub-requests into a single call. A parsing flaw knocks the endpoint's internal request-tracking arrays out of alignment, so one sub-request ends up dispatched under a different handler than intended. Nest the trick and it bypasses the method allow-list. It's rated CVSS 7.5 (High) and was introduced in WordPress 6.9.
How the chain works
Here is the part that matters. The SQL injection is gated, but the route-confusion bug removes the gate.
By abusing the batch endpoint, an attacker feeds their own input into the vulnerable author__not_in parameter without authenticating, therefore converting a bounded, needs-a-foothold SQLi into a pre-authentication one. From there the path is well-trodden: use the injection to read the database, extract admin password hashes, crack a hash offline, log in as an administrator, and upload a webshell or malicious plugin for full code execution.
Neither bug is RCE by itself. The edge between them is.
The prioritisation trap
Now look at how this lands in a typical vulnerability queue.
You have two findings. One is scored 7.5. One is scored 9.1. Sorted by severity, the 9.1 sits near the top of a very long Critical list, flagged as "facilitated" and easy to rationalise as needs a vulnerable plugin, we'll get to it. The 7.5 sits lower, filed as a High to be scheduled in the normal cycle. A queue built on CVSS, handled one CVE at a time, treats these as two unrelated backlog items and gets both of them wrong.
It gets the ranges wrong, too. CVE-2026-60137 affects 6.8 and up. CVE-2026-63030 only affects 6.9 and up. A scanner or triage process that keys on the RCE CVE alone will correctly flag your 6.9 and 7.0 sites, while silently missing the 6.8 installs that are still exposed to the SQL injection. The affected populations don't line up, so a single-CVE view can't see the whole exposure.
None of the individual signals are wrong. The 7.5 is a defensible score for route confusion in isolation. The 9.1 is a defensible score for a facilitated SQLi. The failure is structural: severity describes a node, and the risk lives on the edge. Chained vulnerabilities form real attack paths that any single-issue view rates as manageable, right up until someone strings them together and the whole thing becomes an anonymous HTTP request that ends in a webshell.
What to do
- Patch now. Update to 7.0.2 (7.0 branch), 6.9.5 (6.9 branch), or 6.8.6 (6.8 branch). WordPress 7.1 beta users should move to beta2. Workarounds are not recommended.
- Don't under-scope 6.8. The 6.8.6 release exists specifically because that branch is exposed to the SQL injection even though it escapes the full RCE chain. Inventory against the SQLi range, not just the RCE range.
- Verify the auto-update actually landed. Forced updates fail silently on hosts with locked-down file systems or custom update controls. Check Dashboard → Updates for the real installed version on every internet-facing instance.
- Add WAF coverage as defence-in-depth. Cloudflare and Wordfence shipped protective rules on 17 July. Watch for anomalous traffic to /wp-json/batch/v1 and SQL-injection patterns in REST requests. This buys time; it is not a substitute for patching.
- Assume-breach on anything that was exposed and unpatched. Because the endgame is admin takeover, hunt for new administrator accounts, unexpected plugin uploads, modified core files, and injected redirects. Rotate admin credentials and any secrets in wp-config.php if you suspect compromise.
The takeaway
WP2Shell is not an interesting vulnerability because WordPress is popular, or because RCE is scary. It's interesting because it's a clean demonstration of a gap most vulnerability programmes still carry: we score, rank, and remediate one CVE at a time, and attackers compose. The two halves of this chain were rated, ranged, and reported separately, and the danger only becomes obvious when you model how they connect.
That's the shift from severity to exposure. A severity view asks how bad is this vulnerability? An exposure view asks what can an attacker actually reach, and how? The first sorts a list. The second finds the attack path. For a vulnerability like WP2Shell where neither number tells you to move, but the combination is a pre-auth RCE on a default install, only by understanding the exploit path gets you to the right answer in time.