/wp-json/batch/v1 and ?rest_route=/batch/v1 at your WAF. Scroll down for a version checker, patch steps, WAF rules, detection scripts and indicators of compromise.
Is my site affected? (Instant checker)
Type your exact WordPress version (find it in Dashboard → Updates, or the bottom-right of your admin screen) and hit check. Everything runs in your browser — nothing is sent anywhere.
Prefer an external second opinion? Searchlight Cyber (the team that found the bug) runs a free tester at wp2shell.com that probes your live site. You can also check our wp2shell vulnerability checker tool.
The facts at a glance
| Item | Detail |
|---|---|
| Name | WP2Shell (a.k.a. "wp2shell") |
| CVE IDs | CVE-2026-63030 (REST API batch-route confusion → RCE) chained with CVE-2026-60137 (SQL injection in core) |
| Type | Unauthenticated / pre-auth Remote Code Execution |
| Preconditions | None. Works against a stock install with zero plugins. |
| Affected (RCE) | WordPress 6.9.0–6.9.4 and 7.0.0–7.0.1 (plus 7.1 beta1) |
| Affected (SQLi only) | WordPress 6.8.0–6.8.5 — vulnerable to CVE-2026-60137 but not the full RCE chain |
| Not affected | Anything below 6.8.0 |
| Fixed in | 7.0.2, 6.9.5, 6.8.6 (released 17 July 2026) |
| Entry point | The REST batch endpoint: /wp-json/batch/v1 |
| Discovered by | Adam Kues, Searchlight Cyber |
| Exposure | WordPress powers 500M+ sites; forced auto-updates were enabled for affected installs |
How WP2Shell actually works
WordPress 6.9 introduced a REST batch endpoint — a convenience feature that lets a client bundle several API calls into one request. The flaw is a route-confusion bug: the batch handler can be tricked into resolving inner requests to routes it shouldn't, in a context where the normal permission checks don't apply the way you'd expect.
On its own that's a nasty logic bug. The problem is what it can reach: a second vulnerability, an SQL injection in core (CVE-2026-60137), that an anonymous request can now touch through the batch route. Chain the two together and an attacker walks a completely unauthenticated request from "hello, I'm nobody" all the way to running SQL — and from there to code execution on the server. That's the "2shell" in the name: WordPress to shell, no account needed.
The reason the security community is treating this as a five-alarm fire isn't cleverness — it's reach. There is no plugin dependency, no unusual configuration, no authenticated user required. If you're on an affected version and reachable from the internet, you're in scope. That's why WordPress.org took the rare step of forcing auto-updates.
Patch it — the 5-minute fix (do this first)
Patching is the only real fix. Mitigations below are stopgaps; this is the cure.
- Back up first. Snapshot your database and files (your host's one-click backup is fine). Core updates rarely break anything, but you want a rollback point.
- Go to Dashboard → Updates. If WordPress already auto-updated you (many sites did), you'll see you're on 7.0.2 / 6.9.5 / 6.8.6 — you're done, jump to detection to make sure you weren't hit before the patch landed.
- Click "Update Now." Land on the fixed release for your branch:
- On the 7.0 branch → 7.0.2
- On the 6.9 branch → 6.9.5
- On the 6.8 branch → 6.8.6 (fixes the SQLi; consider moving to a current branch after)
- Confirm the version. Don't assume — check the number in your dashboard footer after the update completes.
Prefer the command line? With WP-CLI:
That last command is doing double duty: verify-checksums compares your core files against WordPress.org's official hashes, so if an attacker modified a core file before you patched, it'll flag the mismatch.
Can't patch right now? Emergency mitigations
Maybe you have a fragile custom build, a change-freeze, or 200 sites to coordinate. These buy you time — they are not a permanent fix, and each can interfere with legitimate REST traffic. You must block both forms of the route; blocking only the pretty path leaves the query-string version wide open.
Option A — Block at your WAF / CDN
Block requests to both of these:
If you're behind Cloudflare, you already have automatic protection — Cloudflare deployed managed WAF rules for both CVEs across all plans, including free, for proxied sites. It's still worth patching; a WAF rule is a filter, not a fix.
Option B — Drop-in "must-use" plugin (no WAF needed)
Create wp-content/mu-plugins/block-batch.php with this. It rejects anonymous calls to the batch route while leaving logged-in admin traffic alone:
mu-plugins load automatically and can't be deactivated from the dashboard, which is exactly what you want for an emergency control. Delete the file once you've patched.
Were you already hit? Detection & log-hunting
Because public exploit code circulated quickly, patching doesn't answer the real question: did someone reach me first? Here's how to check.
1. Hunt your access logs for batch-endpoint abuse
The earliest signal is traffic to the batch route, especially with suspicious SQL-ish parameters. On most Apache/Nginx hosts:
Anomalous author__not_in / author_exclude values containing SQL keywords are one of the cleanest early indicators of an exploitation attempt against this chain.
2. Look for dropped web shells
Successful exploitation typically drops a PHP payload. Scan for recently-changed or suspicious PHP files:
3. Verify core integrity
4. Check the database for injected admin users / options
Indicators of Compromise (IOC)
The following are associated with public WP2Shell proof-of-concept tooling. Treat them as leads, not proof — they're trivial for an attacker to change, and some payloads self-delete after running, so their absence doesn't clear you. Use them to prioritise investigation, alongside the log and file checks above.
| Category | Indicator |
|---|---|
| Target endpoints | POST to /wp-json/batch/v1 or /?rest_route=/batch/v1 |
| Request body | Nested requests[] arrays with malformed http:// / http:: paths |
| SQLi markers | author_exclude / author__not_in values containing SELECT, IF, SLEEP, SUBSTRING, CHAR_LENGTH |
| User-Agents | wp2shell-check/1.0, wp2shell-poc/1.0 |
| Multipart boundary | Boundaries beginning ----wp2shell |
| Dropped files | Plugin ZIPs named wp2shell-<8-hex>.zip; files/dirs under wp-content/plugins/wp2shell-* |
| Command channel | Requests containing ?tok=<token>&c=<command>; cleanup requests with &rm=1 |
| Response markers | Responses containing WP2SHELL_OUT_START / WP2SHELL_OUT_END |
If you find evidence of compromise: assume full server compromise. Patching does not evict an attacker who already has a shell. Take the site offline, rotate all secrets (database password, wp-config.php salts, API keys, admin passwords), restore from a known-clean pre-incident backup, then patch before bringing it back. When in doubt, engage an incident-response professional.
Public PoC code & research resources
| Resource | Type | What it is |
|---|---|---|
| 0xsha/wp2shell | PoC (Python) | Single-file, stdlib-only tool that unifies several public PoCs; forges a post via the UNION confusion, creates an admin, and drops a token-gated webshell. Verified end-to-end in-lab. |
| Icex0/wp2shell-poc | PoC (Python) | Independent PoC for the batch-route SQLi chain with three modes: check (confirms the SQLi path), read (DB read), and shell (plugin-backed command shell). |
| mverschu/CVE-2026-63030 | PoC (Exploit) | PoC exploit for the unauthenticated RCE; documents affected 6.9.0–6.9.4 / 7.0.0–7.0.1 and fixes in 6.9.5 / 7.0.2. |
| ProjectDiscovery Nuclei template | Detection | Community Nuclei template for non-intrusive detection of WP2Shell across many hosts at once (search the repo for “wp2shell” / CVE-2026-63030). |
| ZSec – Code Trace Deep Dive | Analysis | Line-by-line trace of how the batch route confusion reaches the author__not_in SQL injection. Best read for understanding the root cause. |
| Rapid7 – Emergent Threat Report | Analysis | Vendor breakdown of the chain, exploitation status and detection guidance. |
| Searchlight Cyber advisory | Primary source | The original disclosure from the team (Adam Kues) that found the bug. |
| wp2shell.com | Live checker | Searchlight's hosted tool that tests whether a live instance is exposed. Occasionally offline under load. |
Repositories can be renamed or taken down without notice, and GitHub may remove PoCs that violate its policies — if a link 404s, search GitHub for “wp2shell” or “CVE-2026-63030” for current mirrors. We only link to code already public and widely cited by mainstream security outlets.
WP2Shell FAQ
What WordPress versions are vulnerable to WP2Shell?
The full remote code execution chain affects WordPress 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1. WordPress 6.8.0–6.8.5 is affected by the underlying SQL injection (CVE-2026-60137) but cannot be driven to full RCE because the vulnerable batch route was introduced in 6.9. Versions below 6.8.0 are not affected. Fixed releases are 7.0.2, 6.9.5 and 6.8.6.
What are the CVE numbers for WP2Shell?
WP2Shell is a chain of two CVEs: CVE-2026-63030 (REST API batch-route confusion leading to RCE) and CVE-2026-60137 (SQL injection in WordPress core). They are exploited together.
Is there a public WP2Shell PoC or exploit?
Yes. Public exploit code and testing tools circulated shortly after disclosure on 17 July 2026, which is why patching immediately is critical. Searchlight Cyber, who discovered the flaw, deliberately withheld deep technical detail at disclosure to give defenders time, but working exploits are now in the wild.
Do I need a plugin to be vulnerable?
No. This is a WordPress core vulnerability. A stock install with no plugins on an affected version is exploitable by an anonymous, unauthenticated attacker.
I'm on Cloudflare — am I safe?
Cloudflare deployed managed WAF rules covering both CVEs for proxied sites across all plans, including free, which blocks known exploitation patterns. That's a strong safety net, but it's a filter, not a fix — you should still update WordPress to the patched version.
How do I know if my site was already hacked?
Search your access logs for POST requests to /wp-json/batch/v1 with SQL-like author_exclude / author__not_in parameters, scan for recently-modified or web-shell PHP files, run wp core verify-checksums, and check for unfamiliar administrator accounts. See the detection section above for exact commands.
