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 identifyingUser-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, 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 (High) |
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.3 |
6.9.4 |
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.xcarries the SQL injection (CVE-2026-60137) only — not the RCE chain.- CVE-2026-3906 was only partially fixed in
6.9.2/6.9.3and fully fixed in6.9.4, while the wp2shell chain isn't closed until6.9.5. So a6.9.4site reads patched for the Notes bug but still vulnerable to the RCE chain.
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.
- Passive WordPress fingerprinting across four public version vectors:
<meta name="generator">on the homepage/readme.html- RSS / Atom feed
<generator>tag /wp-links-opml.php(OPML)
- Cross-checks the vectors. If they disagree on the version, or a pre-release build (beta/RC/alpha) is seen, the verdict is forced to UNKNOWN — the scanner never reports a possibly-false Patched from conflicting evidence.
- REST API surface discovery — reads
/wp-json/to see whether WordPress advertises thebatch/v1namespace 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. - Per-CVE verdict — the detected version is classified independently against
each tracked CVE (branch-aware), plus outputs:
- a client-ready HTML report (print → PDF),
- a JSON record (pipeline/EAV-friendly),
- a draft notification e-mail.
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 ops@cyberssl.co.uk). 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 this chain |
NOT_AFFECTED |
Version predates the flaw (< 6.9.0) |
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-mail
These are generated from fabricated data to illustrate the report format. They are not the result of scanning any real site.
- Take a verified backup (DB + full file tree) first.
- Update WordPress core to the fixed release for your branch — 6.8.6 / 6.9.4 / 6.9.5 / 7.0.2 — or later.
- Confirm the site and admin still work.
- Assume compromise if the host was exposed while vulnerable — run a compromise
assessment across themes, plugins,
uploads/,wp-config.php, wp-cron, and admin accounts; run file-integrity checks against known-good core. - Add defence-in-depth at the edge (WAF rules, restrict
/wp-json/batch/v1). - Rotate secrets (auth salts, admin/DB passwords, API keys) if compromise is suspected.
- Re-scan to confirm the verdict now reads Patched.
- WordPress GitHub Security Advisory — wp2shell (GHSA-ff9f-jf42-662q)
- WordPress GitHub Security Advisory — Notes REST API (GHSA-6x83-fcf5-r65g)
- NVD — CVE-2026-3906
- Rapid7 — ETR: CVE-2026-63030 wp2shell
- VulnCheck — WP2Shell (CVE-2026-63030 & CVE-2026-60137)
- WordPress releases (6.8.6 / 6.9.4 / 6.9.5 / 7.0.2)
This tool is for authorised security testing only. You are responsible for ensuring you have permission to scan any target. Unauthorised scanning may be unlawful. The authors accept no liability for misuse.