Blackbox, non-intrusive detector for wp2shell (WordPress core pre-auth RCE, CVE-2026-63030 / CVE-2026-60137).
Project description
wp2shell-detect
Blackbox, non-intrusive detector for wp2shell — the pre-authentication RCE chain in WordPress core disclosed on 2026-07-17:
- CVE-2026-63030 — route confusion in the REST batch endpoint
(
/wp-json/batch/v1): a sub-request whose path failswp_parse_url()lands in the validation array but not the handler array, desynchronising the two. - CVE-2026-60137 — SQL injection in
WP_Query'sauthor__not_inhandling, reachable pre-auth through that route confusion.
Chained, they let an anonymous attacker forge an administrator account and drop a plugin webshell — on a default install, no plugin or theme required.
Full technical writeup: https://own2pwn.fr/articles/appsec/wp2shell-wordpress-rce
What this tool does (and does not)
It detects exposure. It does not exploit anything. Every request it makes is a normal, read-only request a crawler could send:
- Fingerprints the WordPress core version from many public sources, so a
hardened install that strips the
generatortag is still identified: thegeneratormeta tag, core asset?ver=(on/wp-includes/and/wp-admin/assets, which track core), the RSS feed generator (?v=),/wp-links-opml.php, the login page assets,/wp-includes/version.php,readme.html, and the/wp-json/index. Sources are cross-checked and a confidence (highwhen ≥2 agree) is reported. - Compares the version to the known-vulnerable ranges.
- Checks the REST batch route with a benign
OPTIONSrequest (and the REST index) — which returns the route schema without ever invoking the handler — to tell whether an edge mitigation is in place. - Discovers headless WordPress back-ends. If the target front isn't WP but
references a WordPress host on the same domain (via CSP, the
Linkheader or asset URLs — common with headless/Next.js fronts), the tool reports it, and--discoverscans it too.
No request triggers the vulnerability. No POST /wp-json/batch/v1, no
author_exclude, no SQL. If the core version is hidden, the tool says so rather
than guessing (confirming wp2shell blindly would require exploiting it).
Resilience: retries with backoff, gzip/deflate decoding, https→http
fallback, cross-host redirect following, a browser-like User-Agent (overridable),
and parallel multi-target scanning.
Vulnerable ranges
| Range | Exposure | Fixed in |
|---|---|---|
| 6.8.0 – 6.8.5 | SQL injection only (no RCE) | 6.8.6 |
| 6.9.0 – 6.9.4 | pre-auth RCE | 6.9.5 |
| 7.0.0 – 7.0.1 | pre-auth RCE | 7.0.2 |
Install
pip install wp2shell-detect
Or run it straight from source (single file, no dependencies, Python 3.8+):
git clone https://github.com/own2pwn-fr/wp2shell-detect
./wp2shell-detect/wp2shell_detect.py https://example.com
Usage
Zero dependencies. Python 3.8+.
wp2shell-detect https://example.com
wp2shell-detect --json https://a.example https://b.example
wp2shell-detect --targets hosts.txt --workers 16 # parallel sweep
wp2shell-detect --discover https://front.example # follow headless WP
(Installed from PyPI, the command is wp2shell-detect. From a clone, call
./wp2shell_detect.py with the same arguments.)
Options: --json, --discover, --workers N, --timeout S, --user-agent UA,
--insecure, --targets FILE. Exit code 2 if any target is found vulnerable
(handy in CI / cron sweeps).
Headless WordPress
Modern sites often serve a JS/Next.js front on www. and keep WordPress on a
sibling host (wordpress., wp., cms.). Scanning www. then returns
not WordPress — correctly, because that host isn't WordPress. Point the scan at
the WordPress host, or pass --discover to have the tool find and scan it from
the front's CSP / Link header / asset URLs automatically.
Example:
[!!] https://blog.example.com -> VULNERABLE (pre-auth RCE)
version : 6.9.3 (generator meta tag=6.9.3, RSS feed generator=6.9.3)
batch : reachable (/wp-json/batch/v1)
cves : CVE-2026-63030, CVE-2026-60137
note : WordPress 6.9.3 is in the wp2shell pre-auth RCE range. REST batch endpoint reachable — no edge mitigation.
Mitigation
Patch WordPress core to 6.8.6 / 6.9.5 / 7.0.2 or later. If patching must
wait, block the batch route at the edge (/wp-json/batch/v1 and
?rest_route=/batch/v1) or via a must-use plugin hooking rest_pre_dispatch,
then audit for rogue admin accounts and unknown plugins.
Legal
For authorised assessment only. Running this against systems you neither own nor are mandated to test may be illegal in your jurisdiction. You are responsible for how you use it.
License
MIT. See LICENSE. By own2pwn.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wp2shell_detect-1.0.1.tar.gz.
File metadata
- Download URL: wp2shell_detect-1.0.1.tar.gz
- Upload date:
- Size: 11.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4be413afc938eedeb021be9b96c42c3a6d2fe27c4ae8f5f2bab3669700a7e7f4
|
|
| MD5 |
7f6dae8653933f7f175829f9036acd79
|
|
| BLAKE2b-256 |
ca3fd2f5b2735c424c6e9898bf37f52b75fe09685572cd12259cdbfa6abed438
|
File details
Details for the file wp2shell_detect-1.0.1-py3-none-any.whl.
File metadata
- Download URL: wp2shell_detect-1.0.1-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c177c36dcf4ca8875ab7665347d987f205b5f6d855ad5d6e74f2cdb4f37080da
|
|
| MD5 |
45a56c979b561bae61345ccd77f39e9a
|
|
| BLAKE2b-256 |
80190667959aa48239f64fb8b8d6216f662d4e0cb27b61bedb15d49fc68f7925
|