VULNERABILITY

wp2shell

Unauthenticated RCE in WordPress Core Hits Every 6.9 and 7.0 Site. Two chained WordPress core bugs let an anonymous HTTP request run code on a bare install with zero plugins.

Matt Lucas  |  July 18, 2026  |  6 min
Editorial hero illustration
6.9–7.0
versions with full RCE chain
2 CVEs
chained: batch route + SQLi
500M+
total WordPress install base
17,000+
sites one prior WP crew hit
TL;DR
  • What: Two chained WordPress core flaws (CVE-2026-63030 REST batch-route confusion and CVE-2026-60137 SQL injection), dubbed wp2shell, let an unauthenticated attacker run code on a default install.
  • Impact: Any WordPress 6.9.0–6.9.4 or 7.0.0–7.0.1 site is exploitable from a single anonymous HTTP request, with no plugins and no login required, and a working PoC is already on GitHub.
  • Fix / mitigation: Update to 6.9.5, 7.0.2, or 6.8.6 (SQLi only); if you can't patch, block /wp-json/batch/v1 and rest_route=/batch/v1 at the WAF.
  • Who's at risk: Every site running WordPress 6.9 or 7.0 for the RCE chain, and 6.8.0–6.8.5 for the SQL injection alone.

An anonymous HTTP request can run code on a WordPress site. The bug is in core, so a bare install with zero plugins is exploitable. Every 6.9 and 7.0 site was in range until Friday, when WordPress shipped 6.9.5 and 7.0.2 and pushed them through forced auto-updates. Since then the full mechanism has been published and a working proof-of-concept has gone up on GitHub, so the window between disclosure and mass scanning is effectively closed.

wp2shell is two bugs, not one. CVE-2026-63030 is a REST API batch-route confusion, found by Adam Kues at Assetnote (Searchlight Cyber's attack surface management arm) and reported through WordPress's HackerOne program. CVE-2026-60137 is a SQL injection in core, reported separately by TF1T, dtro, and haongo. Chained, they take an anonymous request all the way to code execution. Searchlight's writeup states the attack has "no preconditions and can be exploited by an anonymous user."

Who is exposed to what

The two bugs do not reach the same versions, and that split is the whole story of your exposure. The SQL injection goes back to 6.8. The batch-route confusion, the half that turns a bounded injection into unauthenticated RCE, only exists from 6.9 onward. So a 6.8 site is not exploitable for RCE through this chain, which is exactly why 6.8.6 patches the injection alone.

Searchlight estimates over 500 million websites run WordPress, but that is the total install base, not the exposed set. The RCE chain only exists from 6.9, which shipped December 2, 2025. Every site on the code-execution path is running a release less than eight months old, and no advisory says how many that is. WordPress has not confirmed whether the forced push reaches sites that turned auto-updates off, so check what you are actually running rather than assume it landed.

How the chain works

The chain is two small mistakes stacked. The injection lives in WP_Query's author__not_in parameter: hand it a string instead of an array and the check that expects an array is skipped, dropping the raw value into the query. Reaching that parameter without a login is the batch endpoint's job. WordPress's /wp-json/batch/v1 route runs several sub-requests in one call and tracks them in two parallel arrays; an error in one sub-request knocks the arrays out of step by one, so a request executes under a different request's handler. Nested, that confusion walks past the endpoint's allow-list and lands attacker input in the vulnerable query, unauthenticated. The batch endpoint has shipped since 5.6 in 2020; the confusion that abuses it is new to 6.9.

Don't trust the CVSS label on either CVE

WordPress's advisory rates the RCE chain Critical, but its CVE record scores it 7.5 (High), crediting data access alone rather than the integrity and availability loss you would expect from code execution. Meanwhile the SQL injection scores higher than 9.1 (Critical). The bug everyone is calling a critical RCE is, by its own number, the lesser of the two. Track both CVEs, not the label on either.

The one condition that narrows the blast radius

The code-execution path works only when the site is not running a persistent object cache, per Cloudflare, which shipped WAF rules alongside the disclosure. A default install has no such cache, so default-install exposure stands. A site fronting WordPress with Redis or Memcached as a persistent object cache may be off this particular path, but that is a side effect, not a fix, and it does nothing for the SQL injection. With CVE IDs assigned, scanners can finally see it: Rapid7 says authenticated checks for InsightVM and Nexpose land July 20. It is not on CISA's KEV catalog, which requires confirmed exploitation, and none has been reported as of July 18.

That comfort is thinner than it reads. Mass exploitation of WordPress is an industry. Before its server leaked in June, the WP-SHELLSTORM crew got into more than 17,000 sites off a single caching-plugin flaw that was already public, already patched, and only worked on a non-default setting. This one is public, patched, and works on the default setting.

If you can't update today

Every mitigation Searchlight offers comes down to keeping anonymous callers off the batch endpoint. All are stopgaps until you update, and all can break legitimate integrations, so validate before you deploy them wide.

The disclosure was a race, and the patch is the map

WordPress core is open source and the release names the files it changed. Searchlight held its technical writeup, but within a day other researchers read the patch, published the mechanism, and put an exploit on GitHub — the exact outcome the disclosure was racing. You cannot ship the fix without shipping the map to the bug. The only lever left is how fast the patch reaches sites, and WordPress pulled it hard on Friday. The exploit is now public and the update is still rolling.

Questions about your exposure?

RedEye Security provides assessments for organizations that need to understand their real risk.

Talk to us