wp2shell is a pre-auth RCE chain in WordPress core (CVE-2026-63030 + CVE-2026-60137). Here's what it is, who's affected, and how to protect your site.

In July 2026, WordPress shipped an emergency security release for a vulnerability that breaks one of the reassurances site owners have leaned on for years: "I don't run risky plugins, so I'm probably fine." wp2shell doesn't need a plugin. It doesn't need a login. A single anonymous HTTP request to a default, stock WordPress install can end in remote code execution on the server. That is about as bad as a web vulnerability gets, and it lives in WordPress core itself.
This article explains what wp2shell actually is, which versions are exposed, how the attack works at a conceptual level, and the concrete steps to protect a WordPress site. The short version is "update now," but there's enough nuance in the version ranges, the mitigations, and the post-compromise cleanup that it's worth understanding the whole picture, especially if you manage more than one site.
TL;DR
- wp2shell is a pre-authentication remote code execution (RCE) chain in WordPress core, made of two flaws: CVE-2026-63030 (a REST API batch-route confusion, rated Critical) and CVE-2026-60137 (a SQL injection in
WP_Query, rated High). Chained, they let an anonymous attacker run code on a stock install with no plugins.- The full RCE chain affects WordPress 6.9.0–6.9.4 and 7.0.0–7.0.1. It's fixed in 6.9.5 and 7.0.2 (and 7.1 beta2). The SQL injection alone reaches back to 6.8 and is fixed there in 6.8.6.
- Update immediately. WordPress enabled forced auto-updates for affected versions, but you should verify the version that actually installed rather than assume the background update completed.
- The code-execution path only works when the site is not running a persistent object cache. That narrows exposure but is a side effect, not a fix, and it does nothing for the SQL injection.
- Public exploits, active mass scanning, and confirmed in-the-wild exploitation followed within days. If an affected version was live after July 17, 2026, check for unfamiliar admin accounts, unexpected plugins, and modified files even after patching.
wp2shell is the nickname researchers gave to a vulnerability chain in WordPress core, disclosed on July 17, 2026. It isn't a single bug. It's two separate weaknesses that are each limited on their own but become severe when combined. Together they allow unauthenticated remote code execution: an attacker with no account and no valid credentials can make WordPress run code of their choosing on the underlying server.
The chain was discovered by Adam Kues at Assetnote, the attack surface management arm of Searchlight Cyber, and responsibly disclosed through WordPress's HackerOne program. A separate, companion SQL injection was reported by researchers credited as TF1T, dtro, and haongo. WordPress fixed everything in a coordinated security release.
The two flaws carry official CVE and GitHub Security Advisory (GHSA) identifiers:
| CVE | GHSA | Type | Severity |
|---|---|---|---|
| CVE-2026-63030 | GHSA-ff9f-jf42-662q | REST API batch-route confusion (combined with the SQLi to reach RCE) | Critical |
| CVE-2026-60137 | GHSA-fpp7-x2x2-2mjf | Facilitated SQL injection in WP_Query |
High |
One quirk worth flagging: the CVE most people call "the critical RCE," CVE-2026-63030, was assigned a CVSS base score of 7.5 (High) in its CVE record, even though WordPress's own advisory rates it Critical. The scoring credits the injection's direct reach into the database and treats the route confusion on its own as a parsing flaw, which undersells what the two do together. The practical takeaway: don't judge urgency by the number on either CVE in isolation. Track both, and treat the chain as critical.
You don't need to read exploit code to understand why this chain is dangerous. It comes down to two small mistakes that line up into one big one. Full technical details and working proof-of-concept exploits are now public, so there's no benefit in withholding the concept, but this section stays at the level of "how the pieces fit," not a how-to.
WordPress ships a REST API endpoint at /wp-json/batch/v1 (also reachable as ?rest_route=/batch/v1) that lets a client bundle several API sub-requests into a single HTTP call. It's a legitimate performance feature that has shipped in core since WordPress 5.6 back in 2020.
The batch processor runs those sub-requests and tracks them across two parallel internal arrays. The flaw (CVE-2026-63030) is that an error in one sub-request can knock those arrays out of alignment by one, so a request ends up being handled by a different request's handler than the one it was checked against. Nested carefully, that confusion walks an attacker's input past the permission checks that would normally stop it. In effect, the batch endpoint becomes a way to reach internal handlers without authenticating. The batch feature is old; the confusion that abuses it is new to 6.9, which is why the RCE chain only exists from that version on.
Once an attacker can reach internal handlers unauthenticated, they aim at the second flaw. CVE-2026-60137 is a SQL injection in the author__not_in parameter of WP_Query, WordPress's core query class. The parameter expects an array. Hand it a plain string instead, and a check that assumes an array gets skipped, dropping the raw value straight into the database query.
On its own, that injection is normally gated behind authentication. The batch-route confusion is what removes the gate. Chained, an anonymous request slips its input into a vulnerable database query, and from there the attackers escalate to running code on the server, typically by writing a web shell or installing a malicious plugin.
There's a single meaningful precondition. According to Cloudflare, which shipped WAF rules alongside the disclosure, the code-execution path only works when the site is not using a persistent object cache. A default WordPress install has no persistent object cache, so default installs are fully exposed.
A site fronting WordPress with Redis or Memcached as a persistent object cache may be off this particular RCE path. Treat that as a happy accident, not a defense. It does not patch the underlying bugs, it does not protect against the SQL injection, and it isn't something to rely on in place of updating. If your object cache configuration ever changes, so does your exposure.
The two flaws don't reach the same versions, and that distinction decides who is exposed to what. The SQL injection is older and broader; the batch-route confusion (the half that turns a bounded injection into unauthenticated RCE) is newer and narrower.
| Version range | Exposure | Fixed in |
|---|---|---|
| Below 6.8 | Not affected | — |
| 6.8.0 – 6.8.5 | SQL injection only (no RCE chain) | 6.8.6 |
| 6.9.0 – 6.9.4 | Full RCE chain | 6.9.5 |
| 7.0.0 – 7.0.1 | Full RCE chain | 7.0.2 |
| 7.1 beta | Both flaws | 7.1 beta2 |
A few things fall out of this table. A 6.8 site is not exploitable for RCE through this chain, which is why 6.8.6 patches the injection alone. Every site exposed to the code-execution path is running WordPress 6.9 or 7.0, both relatively recent releases (6.9 shipped in December 2025), so this is very much a "current and well-maintained sites" problem, not a legacy one. If you diligently updated to 7.0, you were briefly in the most exposed group, which is a frustrating reversal of the usual advice but doesn't change the fix: get to a patched release.
Updating WordPress core is the fix. There is no configuration you can leave in place of it.
Because of the severity, WordPress.org enabled forced automatic updates through the auto-update system for sites running affected versions. That's good, but it's not a guarantee for every site. WordPress hasn't stated definitively whether the forced push reaches sites that disabled auto-updates, and some hosting setups and version-controlled (Git-based) deployments block background updates entirely. So verify rather than assume.
To check the version you're actually running, open Dashboard → Updates in wp-admin and read the installed version. On any site with SSH and WP-CLI, confirm it from the command line:
wp core versionIf that returns 6.9.5, 7.0.2, or higher, the core fix is in place. Check every environment separately, staging and production, and every site if you run more than one. A single missed install is enough.
Important: If your deployment pipeline pins the WordPress core version (common with Git deploys and composer-managed installs), an auto-update can't help you. Bump the pinned version to a patched release and redeploy.
Sometimes an update can't go out this minute: a change freeze, a fragile custom build, a client site you can't reach fast enough. Every stopgap below comes down to the same idea, keeping anonymous callers off the batch endpoint, and every one of them is temporary. They can also break legitimate integrations that use the REST API, so treat them as emergency measures and remove them once you've updated.
/wp-json/batch/v1 and the query-string form ?rest_route=/batch/v1. Both have to go: a rule covering only the /wp-json path leaves the query-string route wide open. Cloudflare has stated its managed WAF blocks the chain for sites behind it, and other WAF vendors shipped rules quickly after disclosure.rest_pre_dispatch and returns a 401 for anonymous requests to /batch/v1, leaving the rest of the REST API alone. It's a targeted version of option 2.None of these replace updating. They buy you hours, not a resolution.
Before you push a core update or drop in a WAF rule on a live site, take a fresh backup. Security updates are usually smooth, but an emergency change on production is exactly when you want a clean restore point in reach. MagicWP lets you take an on-demand backup before any change and restore it with one click, which is the safe pattern here: snapshot, update, verify, and roll back instantly if anything misbehaves. If you run your own stack, make sure you have both database and files, and that you've actually tested that the restore works.
Patching stops future exploitation. It does nothing about a break-in that already happened. If an affected version (6.9.0–6.9.4 or 7.0.0–7.0.1) was reachable from the internet at any point after July 17, 2026, assume you were scanned and check for compromise even after you've updated. Public exploits and mass scanning appeared within days, and exploitation in the wild has been confirmed, with both CVEs added to the U.S. CISA Known Exploited Vulnerabilities catalog on July 21, 2026.
Here's what defenders and security researchers observed attackers doing after a successful exploit, which doubles as your checklist of what to look for:
eval, passthru, or base64_decode on request parameters), and files that don't belong to any plugin or theme you installed.wp-config.php and authentication keys for exfiltration. If there's any sign of compromise, treat your database credentials and secret keys as burned: rotate database passwords and regenerate the WordPress salts in wp-config.php, which forces all existing sessions to log out./wp-json/batch/v1 or ?rest_route=/batch/v1 that returned HTTP 207 (Multi-Status) or 200 have been a reliable signal of successful exploitation. Purpose-built exploit tools also left telltale user-agent strings referencing wp2shell.If you find any of this, patching alone is not enough. A compromised site needs a full incident response: identify the entry, remove all persistence (accounts, plugins, shells, scheduled tasks), rotate every credential and secret, and ideally restore from a known-good backup taken before the compromise window. When in doubt, rebuild from clean sources rather than trying to surgically clean a live install.
It's tempting to read a core RCE as proof that WordPress is uniquely fragile. It isn't. WordPress runs a very large share of the web, which makes it a permanent target and means any core flaw has an enormous blast radius. The more useful lesson is about how these incidents actually play out.
WordPress core is open source, and a security release names the files it changed. Publishing a fix inevitably publishes a map to the bug. That's not a flaw in the process; it's the unavoidable cost of transparent, coordinated disclosure. Searchlight held its technical write-up, but within about a day other researchers had read the patch, reconstructed the mechanism, and put a working exploit on GitHub. That is the normal shape of these events now. The only thing that decides how one of these plays out is how fast the patch reaches sites before an exploit reaches attackers, which is exactly why WordPress pushed forced updates hard.
For site owners, the durable takeaways are unglamorous but reliable: keep core, plugins, and themes current; put a WAF in front of your site so you have a place to apply virtual patches during the window between disclosure and update; keep backups you've actually tested restoring; and know how to check for compromise, because "I patched" and "I'm clean" are not the same statement. Managed hosting helps here mostly by shrinking the time between a disclosure and a fix reaching your site, and by giving you fast rollback when something goes wrong. On MagicWP, core updates, a managed WAF, isolated site containers, and on-demand backups with one-click restore are handled at the platform level, which is the practical version of "patch fast and have a safety net."
wp2shell is a serious security flaw in WordPress core, disclosed in July 2026, that lets an attacker with no account and no plugins run their own code on a WordPress server by sending it specially crafted web requests. It's built from two bugs chained together (CVE-2026-63030 and CVE-2026-60137), and it affects default installs of WordPress 6.9 and 7.0. WordPress released fixes in versions 6.9.5 and 7.0.2, and the recommended action is to update immediately.
The full unauthenticated RCE chain affects WordPress 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1. It's fixed in 6.9.5 and 7.0.2 (and 7.1 beta2). The companion SQL injection (CVE-2026-60137) reaches further back, affecting 6.8.0 through 6.8.5, and is fixed there in 6.8.6. Versions of WordPress before 6.8 are not affected by either flaw.
Open your WordPress dashboard and go to Updates to see the installed version, or run wp core version over SSH if you have WP-CLI. You're patched if you see 6.9.5, 7.0.2, or later (or 6.8.6 on the 6.8 branch). Don't assume the automatic update completed, especially on Git-based deployments or sites that had auto-updates disabled; verify the actual running version on every site and environment you manage.
A WAF that blocks both /wp-json/batch/v1 and ?rest_route=/batch/v1 can block the attack path and is a reasonable stopgap, but it can break legitimate REST API use and isn't a substitute for updating. A persistent object cache (Redis or Memcached) happens to block the specific code-execution path, but that's a side effect, not a fix: it doesn't patch the bugs and doesn't stop the SQL injection. Update to a patched version regardless of what protections you have in front of the site.
Patch first, then investigate, because updating doesn't undo a break-in that already happened. Look for administrator accounts you don't recognize, plugins you didn't install (including fake security plugins), and recently modified or suspicious PHP files. Check your logs for batch endpoint requests that returned HTTP 200 or 207. If you find anything, rotate your database credentials and regenerate the salts in wp-config.php, and consider restoring from a backup taken before the site was exposed.
Yes. Public proof-of-concept exploits appeared within about a day of disclosure, followed by widespread mass-scanning and confirmed exploitation in the wild. Security firms observed attackers deploying web shells, creating backdoor admin accounts, and installing malicious plugins on vulnerable sites, and both CVEs were added to CISA's Known Exploited Vulnerabilities catalog on July 21, 2026. This is a patch-now situation, not a wait-and-see one.
wp2shell is the kind of vulnerability that cuts through the usual comfort of a lean, well-maintained WordPress site: no plugins to blame, no login to bypass, just core code reachable by anyone. The fix, though, is refreshingly simple. Update to WordPress 6.9.5, 7.0.2, or later (6.8.6 on the 6.8 branch), confirm the version actually installed on every site you run, and if an affected version was ever exposed, check for backdoors before you call it done. Firewalls and object caches can narrow the window, but only patching closes it.
If you'd rather not track core security releases by hand across a fleet of sites, managed WordPress hosting takes most of that weight off you. MagicWP applies core updates, runs a managed WAF, isolates each site in its own container, and keeps daily off-site backups with one-click restore, so when the next core disclosure lands, the patch reaches your sites fast and you have a clean rollback point if you need it. Whatever host you're on, the rule for wp2shell is the same: update now, then verify.
Monthly engineering notes, product updates, and WordPress performance tips. No spam, unsubscribe anytime.