New wp2shell WordPress Core Flaw Lets Unauthenticated Attackers Run Code

Updated July 18, 2026: the two flaws now carry CVE IDs, the full mechanism has been published, a persistent-object-cache condition has surfaced, and a working proof-of-concept is public. The story below reflects all of it.

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 enabled what it calls forced updates through its auto-update system.

wp2shell is two bugs, not one, and both now carry CVE IDs. CVE-2026-63030 is the REST API batch-route confusion; CVE-2026-60137 is a SQL injection in WordPress core. Chained, they take an anonymous request all the way to code execution.

Since Friday, the full mechanism has been published, and a working proof-of-concept has gone up on GitHub.

Adam Kues at Assetnote, Searchlight Cyber’s attack surface management arm, found the batch-route bug and reported it through WordPress’s HackerOne program.

The writeup, published under the name wp2shell, says the attack has “no preconditions and can be exploited by an anonymous user.” The SQL injection was reported separately by TF1T, dtro, and haongo.

Searchlight is still holding its own technical write-up and pointed owners to a checker at wp2shell.com. The reticence is beside the point now: the patch is public, and other researchers read it.

The two bugs do not reach the same versions, and that is the key to who is exposed to what. The 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 on.

So the ranges split:

  • 6.8.0 through 6.8.5: SQL injection only, fixed in 6.8.6
  • 6.9.0 through 6.9.4: full RCE chain, fixed in 6.9.5
  • 7.0.0 through 7.0.1: full RCE chain, fixed in 7.0.2

7.1 beta2 carries both fixes. A 6.8 site is not exploitable for RCE through this chain, which is why 6.8.6 patches the injection alone.

WordPress has not said whether the forced push reaches sites that turned auto-updates off. Check what you are actually running rather than assume it landed.

Searchlight’s post estimates that over 500 million websites run WordPress. That figure is the total install base, not the exposed set: the RCE chain only exists from 6.9, which shipped on December 2, 2025. So every site exposed to the code-execution path is running a release less than eight months old, and no advisory says how many that is.

The chain has two small mistakes. 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 runs under a different request’s handler.

Nested, that confusion walks past the endpoint’s allow-list and lands the attacker’s 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.

The scores are worth reading closely. WordPress’s own advisory rates the RCE chain Critical. Its CVE record scores it 7.5, only High, and the impact metrics credit data access alone, not the integrity or availability loss you would expect from code execution. The injection depends on scores higher than 9.1, Critical.

The bug everyone is calling a critical RCE is, by its own number, the lesser of the two, because the scoring rewards the injection’s direct reach into the database and treats the route confusion on its own as a parsing flaw. Track both CVEs, not the label on either.

One condition 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 not cover the SQL injection.

With CVE IDs assigned, the 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 takes 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 now. Before its server leaked in June, one caching-plugin flaw alone got the WP-SHELLSTORM crew into more than 17,000 sites by its own count, using a bug 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:

  • At a WAF, block both /wp-json/batch/v1 and rest_route=/batch/v1. Both have to go, because a rule covering only the /wp-json path leaves the query-string route open. Cloudflare says its managed WAF now blocks the chain for sites behind it.
  • Disable WP REST API, which kills unauthenticated REST access wholesale.
  • A short drop-in plugin Searchlight publishes that rejects anonymous /batch/v1 requests at rest_pre_dispatch.

WordPress core is open source, and the release names the files it changed. That was always going to be enough. Searchlight held its write-up; within a day, other researchers had read the patch, published the mechanism, and put an exploit on GitHub, which is the exact shape 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 before someone reads it, and WordPress pulled it hard on Friday.

Now the exploit is public, and the update is still rolling. WordPress’s version stats will show how many sites took the patch; scan traffic against batch/v1 will show how many attackers came looking. Whichever curve is steeper decides how this one gets remembered.

Similar Posts