
Non-intrusive detection scanner for the WordPress wp2shell pre-auth RCE chain (CVE-2026-63030 + CVE-2026-60137). Detection-only, no exploitation.
Non-intrusive detection scanner for recent WordPress core vulnerabilities — including the wp2shell pre-authentication RCE chain.
wp2shell_scanner.py fingerprints a WordPress site's core version and reads its
publicly advertised REST API surface, then classifies each tracked core CVE
independently against the detected version. Verdicts are branch-aware — e.g. a
6.8.x site is correctly reported as exposed to the SQL-injection CVE but not to the
full RCE chain. It detects only: no exploit payload is ever sent. Single file, one
dependency (requests), CI-friendly exit codes, and client-ready HTML / JSON / e-mail
reports.
⚠️ Authorised use only. Scan systems you own or are explicitly authorised (in writing) to test. The scanner sends an identifying
User-Agentso blue teams can attribute the traffic — it does not hide.
| CVE | Weakness | Access | Affected core | Fixed in |
|---|---|---|---|---|
| CVE-2026-63030 — wp2shell | CWE-436 — REST batch route confusion → RCE (Critical; CVSS 9.8 WPScan CNA / 7.5 CISA-ADP; GHSA-ff9f-jf42-662q) | Unauthenticated | 6.9.0–6.9.4, 7.0.0–7.0.1 | 6.9.5, 7.0.2 |
| CVE-2026-60137 | CWE-89 — SQL injection in WP_Query author__not_in (Moderate standalone, CVSS 5.9; Critical in-chain; GHSA-fpp7-x2x2-2mjf) | Unauthenticated | 6.8.0–6.8.5, 6.9.0–6.9.4, 7.0.0–7.0.1 | 6.8.6, 6.9.5, 7.0.2 |
| CVE-2026-3906 | CWE-862 — Notes REST API missing authorization (Moderate, CVSS 4.3, GHSA-6x83-fcf5-r65g) | Subscriber+ | 6.9.0–6.9.1 | 6.9.2 |
The wp2shell RCE chain = CVE-2026-63030 + CVE-2026-60137 together → pre-auth
remote code execution on a default install. The scanner reports the chain as exposed
only when both CVEs classify as vulnerable for the detected version.
Branch nuances the scanner gets right:
6.8.x carries the SQL injection (CVE-2026-60137) only — not the RCE chain.6.9 branch, CVE-2026-3906 (Notes) is fixed in 6.9.2, but the wp2shell
chain is not closed until 6.9.5. So 6.9.2/6.9.4 read patched for the Notes
bug yet remain vulnerable to the RCE chain — the scanner therefore only recommends
6.9.5 or later on that branch, never 6.9.4.Operationally important: technical write-ups and a working PoC for wp2shell are already public. Version detection is now the absolute minimum — the priority is patching. Updating closes the vulnerable path but does not remove a backdoor planted before the patch — hence the compromise-assessment step in the report.
This is a core-version scanner. Plugin and theme CVEs — the large majority of the WPScan / Patchstack catalogue — require a live, curated feed and per-plugin version enumeration, and are intentionally out of scope here. Within its lane (recent WordPress core CVEs), it aims to be precise and branch-accurate rather than to duplicate a commercial vulnerability database.
<meta name="generator"> on the homepage/readme.html<generator> tag/wp-links-opml.php (OPML)/wp-json/ to see whether WordPress
advertises the batch/v1 namespace in its REST index. This confirms the
namespace is registered, not that the endpoint is reachable through a WAF.
No batch request and no payload are sent.It does not exploit anything. No SQL-injection payload, no batch-route-confusion request, no attempt to execute code or read data. Detection is version-based plus a passive read of the publicly advertised API surface.
pip install requests
# single target
python3 wp2shell_scanner.py -t https://site.example --authorized
# list of targets (one per line, # comments allowed)
python3 wp2shell_scanner.py -T scope.txt --authorized -o ./reports
# preview the output formats with synthetic data — no network, no auth needed
python3 wp2shell_scanner.py --demo -o ./reports
| Flag | Meaning |
|---|---|
-t, --target | Single target URL or host |
-T, --targets-file | File with one target per line |
--demo | Generate a SAMPLE report/e-mail from synthetic data (no network) |
-o, --output-dir | Output directory (default ./wp2shell_reports) |
--formats | Comma list: json,html,email (default: all) |
--authorized | Authorisation gate — required for live scans |
--timeout | HTTP timeout in seconds (default 12) |
--delay | Seconds between targets (be polite; default 1.0) |
--insecure | Do not verify TLS certificates |
--quiet | Suppress the console summary |
The --authorized flag is an explicit authorisation gate — live scans refuse to run
without it.
| Code | Meaning |
|---|---|
2 | At least one target vulnerable |
1 | At least one target inconclusive / error |
0 | Clean (patched / not affected / not WordPress) |
The notification e-mail recipient is the CSSLTD_CONTACT constant near the top of the
script (default [email protected]). Edit it to your own intake address.
| Verdict | Meaning |
|---|---|
VULNERABLE | Detected version falls inside an affected range — patch immediately |
PATCHED | On a fixed release; not exposed to the tracked CVEs |
NOT_AFFECTED | Version predates the earliest tracked flaw (< 6.8.0 — CVE-2026-60137) |
UNKNOWN | Version could not be confirmed, vectors disagreed, or a pre-release build was seen — verify manually |
NOT_WORDPRESS | No WordPress fingerprint found |
The examples/ directory contains synthetic (--demo) output for a
fictional vulnerable host (example.com, WordPress 6.9.1 — exposed to all three tracked CVEs):
wp2shell_report_example.com.html — the client-ready HTML reportwp2shell_example.com.json — the JSON recordwp2shell_email_example.com.txt — the draft notification e-mailThese are generated from fabricated data to illustrate the report format. They are not the result of scanning any real site.