Home/ Blog/ Security news/ Article
Blog · Security news

wp2shell went from patch to public exploit in a day. Patching is no longer enough.

Public proof-of-concept exploits for the wp2shell WordPress Core RCE (CVE-2026-63030) are live and the mechanism is disclosed. Patch to 6.9.5 or 7.0.2, then

A closing gap between two walls with light rushing through, illustrating the shrinking wp2shell patch window

A public fix is a roadmap, and last week we said the quiet around wp2shell was a head start, not a reprieve. That head start is gone. One day after WordPress shipped the emergency Core release on July 17, the two flaws picked up CVE IDs, the full exploitation mechanism was published, and multiple working proof-of-concept exploits landed on GitHub. By July 19 the watchTowr team reported those exploits already circulating and the earliest live attacks starting to land. If you deferred the update on the assumption that no exploit existed yet, your threat model changed under you: you are now racing internet-wide scanners, not researchers sitting on a private writeup.

wp2shell: from patch to public exploit in about a dayJul 17: Core patch ships. Jul 18: CVEs + mechanism public. Jul 18: GitHub PoCs drop. Jul 19: First ITW signs.wp2shell: from patch to public exploit in about a dayJul 17Core patch shipsJul 18CVEs + mechanismpublicJul 18GitHub PoCs dropJul 19First ITW signs
The window WordPress admins had to patch before working exploits went public.

The compression here is the story. A pre-authentication remote code execution bug in default WordPress Core went from patched to weaponized in roughly a day. That is the planning number to carry forward: for an unauthenticated Core RCE, your patch service level has to be measured in hours, not the weekly maintenance window most teams run. The gap between "a fix is public" and "a working exploit is public" is now short enough that any deferral policy keyed to the old cadence is a policy to get hit.

What the newly public mechanism actually does

When we first wrote this up, the researchers at Searchlight Cyber were holding the technical detail back. Now it is out, and it is worth understanding because it dictates how you hunt. wp2shell is two bugs chained. The base is CVE-2026-60137, a SQL injection reached through WP_Query by way of its author__not_in argument. That argument expects an array; hand it a plain string and the validation waves it through, so attacker-controlled text lands in the query unescaped. On its own the flaw goes back to WordPress 6.8 and lets someone read password hashes straight out of the database.

The second bug, CVE-2026-63030, is the piece that removes the login requirement. WordPress lets the block editor fold several API calls into a single POST to /wp-json/batch/v1, and it keeps the bookkeeping for those folded calls in side-by-side lists. Force an error in one of them and the lists slip out of step by a single slot, which quietly reassigns a later call to a route handler it was never meant to reach. Once a call runs under the wrong handler, the endpoint allowlist stops meaning anything, and an anonymous request has a clean path down to the injection. No account, no plugin, no victim click. A stock install with no plugins at all is exploitable, which is what makes this a Core problem instead of a plugin one.

The object-cache "safety" is a trap, not a control

Our earlier post noted the one condition that narrows exposure: per Cloudflare, the code-execution path only opens on a site with no persistent object cache, and stock WordPress ships without one. Plenty of teams will read that, note that they run Redis or Memcached, and move the patch down their list. That is a mistake for two reasons.

First, the object cache only gates the code-execution step. The SQL injection underneath runs regardless of your cache, and it leaks password hashes. An attacker who cannot pop a shell on your cached site can still walk away with credentials to crack offline and replay, which is the follow-on the public PoCs are built to automate. Second, an object cache is a runtime dependency, not a security boundary. A dropped Redis connection, a failed Memcached lookup, a config change during a deploy: any of these can silently fall back to no cache, and the moment it does, the RCE path is open again. You do not want the thing standing between an anonymous request and code execution on your host to be whether a cache daemon happened to answer.

Patch first, then virtual-patch without breaking the editor

The fix is unambiguous: update to 6.9.5 or 7.0.2 now, and move sites on the 6.8 branch to 6.8.6 to close the injection. If you genuinely cannot patch in the next few hours, virtual-patching at the WAF buys time, and Cloudflare has already pushed rules to every plan. But be precise about the rule. Blanket-blocking the batch route at /wp-json/batch/v1, along with its ?rest_route=/batch/v1 form, also breaks the block editor, which uses the same batch endpoint to bundle saves into one request. The safer rule scopes the block to unauthenticated traffic: reject POSTs to the batch route that carry no valid logged-in cookie or REST nonce, and let authenticated editor traffic through. That keeps Gutenberg working while cutting off the anonymous path the exploit needs.

Now that the mechanism is public, hunt on it

Patching closes the hole. It does not tell you whether you were hit during the window between July 17 and the moment you updated, and with PoCs public since July 18 that window is where the risk lives. The good news is that a public mechanism is also a public detection signature. Look for anonymous POST requests hitting the batch route in your web server logs, whether they arrive as /wp-json/batch/v1 or the ?rest_route=/batch/v1 variant, especially any that predate your own upgrade. Pair that with file integrity monitoring for new or modified PHP under wp-content/uploads, wp-content, and wp-includes: a dropped web shell remains the most common post-exploitation artifact, and it is the same pattern we have watched play out across unpatched CMS campaigns all year. If hashes were pulled through the injection, rotate credentials and force password resets, because a patch does not evict an attacker who already has valid logins.

The uncomfortable part of wp2shell is how ordinary the timeline was. There was no zero-day surprise. The vendor did everything right, disclosed responsibly, and shipped a clean fix with a short embargo. The exploit still went public in a day, because a Core RCE with a public patch is one of the most valuable reverse-engineering targets on the internet. Treat the next one the same way: patch on the clock of the attacker, not the clock of your change-management calendar, and assume the hunt starts the moment the fix lands.

Topics

Frequently asked questions

Are public exploits for wp2shell available now?

Yes. Multiple working proof-of-concept exploits for the wp2shell WordPress Core flaws appeared on GitHub on July 18, 2026, one day after the patch. Some extract and crack password hashes; others claim direct pre-authentication remote code execution. The watchTowr team reported the first signs of in-the-wild exploitation by July 19.

Which WordPress versions are affected and what should I upgrade to?

The full remote code execution chain affects Core 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1. Update to 6.9.5 or 7.0.2. The underlying SQL injection reaches back to 6.8, so sites on that branch should move to 6.8.6.

What are CVE-2026-63030 and CVE-2026-60137?

CVE-2026-63030 is a REST API batch-route confusion bug introduced in WordPress 6.9. CVE-2026-60137 is a SQL injection in the author__not_in parameter of WP_Query. Chained, they let an anonymous request reach code execution on a default install.

Does running Redis or Memcached protect me from wp2shell?

Only partly. A persistent object cache blocks the code-execution path, but the SQL injection still runs and can leak password hashes regardless of the cache. A dropped cache connection also falls back to no cache, reopening the RCE path, so treat the object cache as a runtime dependency, not a fix. Patch anyway.

How do I virtual-patch wp2shell without breaking the block editor?

Do not blanket-block /wp-json/batch/v1, because the Gutenberg editor uses it to bundle saves. Scope the WAF rule to reject only unauthenticated POSTs to the batch route (no valid logged-in cookie or REST nonce), which cuts off the anonymous exploit path while letting authenticated editor traffic through.

How can I tell if my WordPress site was already exploited?

Check web server logs for anonymous POST requests to /wp-json/batch/v1 or ?rest_route=/batch/v1, especially any that predate your upgrade. Use file integrity monitoring to spot new or modified PHP files under wp-content/uploads, wp-content, and wp-includes. If the injection ran, rotate credentials, because patching does not evict an attacker who already has valid logins.

Ready to meet the Guardians?

Deploys fast - agentless for monitoring and cloud, a lightweight agent for deep endpoint security. Just Suriq, standing watch.