## https://sploitus.com/exploit?id=77286AB8-0673-5512-B87F-EF5A2708FFD0
# wp2shell (CVE-2026-63030 + CVE-2026-60137): Independent Root Cause Analysis
WordPress core disclosed wp2shell on 2026-07-17 as a critical, unauthenticated
RCE affecting default installs (WordPress 6.9.0-6.9.4, 7.0.0-7.0.1, zero
plugins required). The original discoverer (Searchlight Cyber) withheld
technical details at disclosure time. This repo documents an independent
root-cause analysis derived entirely from diffing WordPress core source
between the vulnerable (6.9.4) and patched (6.9.5) releases, plus live
verification in a local lab.
Credit: the reproduction here follows the request shape from
[sergiointel/wp2shell-poc](https://github.com/sergiointel/wp2shell-poc),
confirmed byte-for-byte and cross-checked against the real patch diff.
## Contents
- `root_cause_analysis.md` - full writeup: the two chained bugs, exact
vulnerable code, the patch diff, the exploit chain, and live before/after
verification against a real WP 6.9.4 instance.
- `poc_upstream.py` - unmodified copy of sergiointel's PoC.
- `poc_extract.py` / `poc_extract2.py` - a tuned variant (larger `SLEEP()`,
higher timing threshold, longer character-search ceiling) needed to get a
reliable signal in a Dockerized/proxied lab where the upstream script's
default `SLEEP(0.15)` was lost in network jitter.
- `docker-compose.yml` - spin up the same WordPress 6.9.4 + MariaDB lab used
for verification.
- `payload.json`, `response.json`, `response_patched.json` - a hand-crafted
request replicating the PoC's structure and the raw server responses,
before and after applying the 6.9.5 patch files in place.
## Summary of findings
**CVE-2026-63030 (REST batch route confusion, CWE-436):**
`WP_REST_Server::serve_batch_request_v1()` appends to a `$matches[]` array by
simple push, but skips the append when a sub-request's path fails to parse
(e.g. a deliberately malformed `"http://:"` entry). That single skip
desynchronizes `$matches[]` from `$requests[]` by one index for every
following entry. At dispatch time, `$matches[$i]` no longer corresponds to
`$requests[$i]`, so the code ends up executing a request's data under a
*different* route's matched handler than the one it was actually validated
against.
**CVE-2026-60137 (SQL injection):** `WP_Query::get_posts()` only ran
`absint()` sanitization on `author__not_in` inside an `is_array()` branch. A
scalar string value skipped sanitization entirely and was concatenated
directly into `... post_author NOT IN ($value)`.
**Chained:** the route-confusion bug lets an attacker get a request declared
against `/wp/v2/categories` (which doesn't recognize `author_exclude`, so it's
never sanitized) actually dispatched through the **posts** controller (which
does read `author_exclude` and forwards it into `WP_Query`). No authentication
required.
**On the RCE claim:** confirmed live that unauthenticated blind SQL injection
works and can exfiltrate arbitrary DB content, including
`wp_users.user_pass`. Confirmed that a single timing-based extraction pass in
a virtualized lab has meaningful bit-error noise (~94% per-character accuracy
in testing here), and that the technique's final step, turning an extracted
bcrypt hash into working admin credentials, depends entirely on the target's
password strength, not on the vulnerability itself. RCE is a realistic
consequence of this bug, not a guaranteed, automatic one.
## Reproducing
```bash
docker compose up -d
# wait for WordPress install wizard to be reachable on :8890, then complete setup
HTTP_PROXY=http://127.0.0.1:8080 HTTPS_PROXY=http://127.0.0.1:8080 \
python3 poc_extract2.py http://localhost:8890 "SELECT DATABASE()"
```
See `root_cause_analysis.md` for the full technical writeup.
## Responsible Disclosure
WordPress has published official fixes: 6.9.5 and 7.0.2 (7.1 Beta 2 for the
beta branch), released 2026-07-17. Update immediately if you run an affected
version. This repository is published for defensive and educational purposes
after the patch was already public. Only run any of the code here against
systems you own or are explicitly authorized to test.