wp2shell: how two WordPress core bugs chain into unauthenticated RCE (CVE-2026-63030)
A REST batch-route confusion plus an author__not_in SQL injection turn a stock WordPress 6.9/7.0 install into a pre-auth shell. Here's the chain, who's affected, and what to do today.

For a bug to matter at internet scale it has to be reachable, unauthenticated, and sitting on software everyone runs. CVE-2026-63030 is all three: a pre-authentication remote-code-execution chain in WordPress core -- not a plugin -- that a researcher branded wp2shell. It works on a stock install with no account, no plugin, no user interaction. On the world's most-used CMS, that is the rarest and most dangerous shape a vulnerability can take.
Scores as of 19 July 2026live record →
What the flaws actually are
wp2shell is two bugs working together. The first, CVE-2026-63030, is a route-confusion flaw (CWE-436) in the WordPress REST API's batch endpoint -- the one the block editor uses to bundle several API calls into a single request. When a sub-request errors while it is being built, WordPress records the error in one internal list but forgets to add a matching entry to the list that holds each sub-request's permission check. The two lists fall out of step by one, so a request the attacker controls gets dispatched under a different request's permission callback -- walking straight past the check that should have blocked it.
The second bug, CVE-2026-60137, is a SQL injection (CWE-89) in WP_Query's author__not_in parameter. WordPress expected an array of integers there; if a string arrives instead, the sanitisation guard is skipped and the raw value is dropped straight into a NOT IN (...) SQL clause. On its own that flaw needs trusted code to reach it -- which is why the route confusion matters. Chained, the batch bug lets an anonymous attacker steer a crafted author_exclude value into that vulnerable query, delivering the SQL injection with no login at all. On a default install, that read primitive is enough to reach code execution.
The exploitation chain
From an anonymous HTTP request to code execution, the path is short. The attacker sends a single POST to the batch endpoint -- reachable as both /wp-json/batch/v1 and /?rest_route=/batch/v1, so it works even without pretty permalinks -- carrying a batch envelope whose malformed first sub-request triggers the index desync. That smuggles a author_exclude value into the vulnerable query for the unauthenticated SQL injection, and on a default install (no persistent object cache) the chain converts into remote code execution. Follow the stages below, and note the prevent/detect chokepoints at each step -- patching removes the first two entirely.
wp2shell exploitation chain -- anonymous POST to code execution
- Unauthenticated attacker (WP 6.9.0-6.9.4 / 7.0.0-7.0.1) — No account, no plugin, no user interaction. **Prevent:** patch to 7.0.2 / 6.9.5 -- the single most effective control.
- Fingerprint the REST batch endpoint — `GET /wp-json/` for the `batch/v1` namespace, or probe `/?rest_route=/batch/v1`. **Prevent:** WAF-block both endpoint forms if you cannot patch.
- POST a malformed batch envelope — A broken first sub-request path shifts WordPress's internal request/permission arrays by one. **Detect:** anonymous POST to /batch/v1 returning HTTP 207.
- Route/permission desync -- CVE-2026-63030 — Request i runs under request i+1's permission check -- the allow-list is bypassed. CWE-436. **Prevent:** the 7.0.2/6.9.5 fix re-aligns the arrays.
- Smuggled author_exclude -> SQL injection -- CVE-2026-60137 — A string reaches WP_Query's author__not_in past the guard, interpolated raw into NOT IN (...). Unauthenticated, time-based blind. CWE-89.
- Persistent object cache enabled? — The RCE step works only WITHOUT a persistent object cache (the default). With Redis/Memcached object cache, impact is limited to database read.
- SQLi -> code execution as the web-server user — The exact primitive is embargoed by the researcher. Result: full site control and a pivot into the hosting environment.
- Unauthenticated database read (admin hashes, PII) — Even without RCE, the injection dumps the database -- admin password hashes, user data. **Respond:** rotate admin creds and wp-config salts.
- Webshell / admin takeover / full site control — Persistence, then monetization: access resale, SEO spam, coin-miners, WooCommerce card-skimming. **Detect:** new admin users, unexpected plugins.
- Detect & verify (Nuclei / WAF / logs) — Official Nuclei template (safe time-based check) + the anonymous-POST-to-/batch/v1 -> 207 log signal.
Am I affected?
Get the version math right, because 6.8.x is a different story from 6.9+. The RCE chain needs the 6.9+ batch endpoint; the SQL injection alone reaches back to 6.8. If you run 6.8.x you are exposed to the injection but not the full shell, and you only need 6.8.6.
| Branch | SQLi (CVE-2026-60137) | Full RCE chain (CVE-2026-63030) | Fixed in |
|---|---|---|---|
| Below 6.8 | Not affected | Not affected | -- |
| 6.8.0 - 6.8.5 | Affected (SQLi only) | Not affected | 6.8.6 (SQLi fix) |
| 6.9.0 - 6.9.4 | Affected | Affected | 6.9.5 (both) |
| 7.0.0 - 7.0.1 | Affected | Affected | 7.0.2 (both) |
| 7.1 (beta) | Affected pre-beta2 | Affected pre-beta2 | 7.1 beta2 (both) |
Timeline
Disclosed and patched the same day, PoC within 24 hours
- Emergency release + forced auto-updatesWordPress ships 7.0.2 / 6.9.5 / 6.8.6, publishing both advisories and enabling forced auto-updates. Searchlight/Assetnote (Adam Kues) withhold exploit details to give defenders time.
- WAF coverageCloudflare ships managed + free WAF rules; Wordfence pushes a firewall rule to paid tiers.
- Public PoC appearsA working detection PoC lands on GitHub and researchers begin reversing the patch; maturity moves from none-public to functional in about a day.
- No confirmed in-the-wild compromise; not on CISA KEVAuthoritative reporting still shows no confirmed exploitation and no KEV listing -- only opportunistic scanning and unverified early signals.
Exploited in the wild -- what we know and don't
Be precise here, because the vendor chatter is noisier than the evidence. The discoverer's advisory makes no exploitation claim; Rapid7 says it is not aware of publicly confirmed in-the-wild exploitation; The Hacker News notes the bug is not on CISA's KEV catalogue and none had been reported as of 18 July. A couple of vendors mention "first indications" of activity and one reports possible exploitation on disclosure day, but those signals are ambiguous (scanning versus exploitation) and unconfirmed at a primary source. The realistic picture as of 19 July is opportunistic probing plus a public read primitive -- not a confirmed compromise campaign.
Detection & hunting
The catch for log hunters: the malicious parameters live in the POST body, which standard webserver access logs do not record -- they show only the path. So the highest-signal, always-available indicator is any anonymous POST to /batch/v1 that returns HTTP 207, because that endpoint is almost never called without authentication in normal traffic. If your WAF or reverse proxy logs bodies, hunt for author_exclude / author__not_in, SLEEP(, and -- - markers as well. Two deployable detections:
One verified public check plus one author-written log rule. Confirm the endpoint is not called anonymously in your baseline before alerting.
title: WordPress wp2shell REST Batch-Route Exploit Attempt (CVE-2026-63030 / CVE-2026-60137)
status: experimental
description: Anonymous access to the WordPress REST batch endpoint used by the wp2shell pre-auth RCE chain.
logsource:
category: webserver
detection:
batch_endpoint:
cs-method: 'POST'
cs-uri-stem|contains:
- '/wp-json/batch/v1'
- '/batch/v1'
batch_query:
cs-uri-query|contains: 'rest_route=/batch/v1'
condition: batch_endpoint or batch_query
falsepositives:
- Authenticated block-editor / bulk REST batch usage
level: high
tags:
- attack.initial_access
- attack.t1190- Check exposure (on host):
wp core version-- vulnerable band is 6.9.0-6.9.4 / 7.0.0-7.0.1 (RCE) and 6.8.0-6.8.5 (SQLi only). - Check the surface:
curl -s https://TARGET/wp-json/ | grep -i 'batch/v1', and probe both/wp-json/batch/v1and/?rest_route=/batch/v1. - Assume-breach hunt (if you were unpatched and exposed before ~17 Jul): grep access logs for anonymous
POST .../batch/v1returning 207; review for new admin users, unexpected plugins, and.phpwebshells inwp-content/.
What to do now
- Patch. Update to 7.0.2 or 6.9.5 (6.8.6 if you are on 6.8.x). Then verify:
wp core update && wp core version. - If you can't patch immediately, WAF-block anonymous access to BOTH
/wp-json/batch/v1AND/?rest_route=/batch/v1-- the query-string form is the common miss. Cloudflare and Wordfence managed rules already cover it. - Verify the fix. Re-run the Nuclei check and confirm no match; confirm the installed build; a patched host should not return an exploitable 207 to a malformed batch request.
- If you had an exposed, unpatched window, rotate all admin passwords and the wp-config secret keys/SALTS to invalidate sessions, then hunt for webshells and rogue admins. The injection reads admin password hashes, so credential rotation is time-sensitive.
FAQ
What is wp2shell?
author__not_in SQL injection). Together they give an unauthenticated attacker SQL injection and, on a default install, remote code execution on WordPress 6.9.0-6.9.4 and 7.0.0-7.0.1.Is CVE-2026-63030 being exploited in the wild?
Am I safe because WordPress auto-updates?
wp core version and confirm 7.0.2 / 6.9.5 / 6.8.6 or newer -- do not assume.I'm on 6.8.x -- do I need to worry?
Is there a public exploit?
Sources
- WordPress 7.0.2 security release
- WordPress advisory GHSA-ff9f-jf42-662q (batch-route confusion, CVE-2026-63030)
- WordPress advisory GHSA-fpp7-x2x2-2mjf (author__not_in SQLi, CVE-2026-60137)
- Searchlight Cyber -- wp2shell: pre-authentication RCE in WordPress core
- Rapid7 -- ETR: CVE-2026-63030 wp2shell
- The Hacker News -- New wp2shell WordPress core flaw
- Cloudflare -- WAF coverage for the WordPress vulnerabilities
- VulnCheck -- wp2shell analysis
- ProjectDiscovery Nuclei template -- CVE-2026-63030