Sploitus

Exploit for SQL Injection in Wordpress

githubexploit Β· 2026-08-03

Exploit Code

README276 lines
## https://sploitus.com/exploit?id=546B8504-B9CC-540F-BD57-61E9C13C0257
# wpsqli - WordPress SQLi Extractor (CVE-2026-60137)

> A fast, menu-driven database extraction tool exploiting the `author__not_in` SQL 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.

---

## Table of Contents

- [Overview](#overview)
- [Vulnerability Chain](#vulnerability-chain)
- [Affected Versions](#affected-versions)
- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Menu Options](#menu-options)
- [Extraction Modes](#extraction-modes)
- [Performance](#performance)
- [Legal Disclaimer](#legal-disclaimer)

---

## Overview

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

---

## Vulnerability Chain

This tool chains two independent vulnerabilities to achieve unauthenticated database extraction:

### Bug A β€” `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.

```php
// 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) ";
```

### Bug B β€” REST Batch Route Confusion (CVE-2026-63030)

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.

---

## Affected Versions

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

---

## Features

- **No Dependencies:** Pure Python standard library. No `pip install` required.
- **Three-Tier Extraction:**
  - **UNION-based:** Forges a fake `wp_posts` row to extract entire strings in a single HTTP request (instant).
  - **Boolean-blind (Parallel):** If UNION is blocked by object caching, uses 8 parallel threads with binary search to extract ~7 requests per character.
  - **Time-based (Sequential):** Fallback if boolean oracle returns no rows.
- **Full Enumeration:** List databases, tables, and columns.
- **Table Dumping:** Dump individual tables, all tables in a DB, or a full nuclear dump to a timestamped text file.
- **Graceful Interruption:** `Ctrl+C` during extraction stops the current query and returns partial results without crashing the session.
- **Session Persistence:** Connect once and run multiple queries via the interactive menu.
- **URL Sanitization:** Automatically strips paths/queries from host input to prevent endpoint doubling.
- **Crack-Ready Output:** User dumps include `ID|user_login|user_pass` format with bcrypt hashes ready for `hashcat -m 35500`.

---

## Installation

No installation required. Just download and run:

```bash
# 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:**
- Python 3.8+
- No external packages needed (stdlib only)

---

## Usage

Launch the interactive menu:

```bash
python main.py
```

### Example Session

```
============================================================
  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)
```

---

## Menu Options

### Connection

| Option | Description |
|---|---|
| `0` | Connect to a target (sanitizes URLs automatically) |
| `1` | Verify vulnerability and active extraction mode |

### Fingerprinting

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

### Enumeration

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

### Dumping

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

---

## Extraction Modes

The tool automatically attempts three extraction modes in order of speed:

### 1. UNION-Based (Instant)

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.

- **1 request per query**
- Works only when no persistent object cache is active
- Uses `CONCAT(0x7c7c, HEX(CAST(() AS CHAR)), 0x7c7c)` to encode the result

### 2. Boolean-Blind (Parallel)

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

- **~7 requests per character** (parallelized across 8 threads)
- Binary search on ASCII range 32-126
- Pre-scans string length via binary search before extraction
- Progress displayed as chars complete

### 3. Time-Based (Sequential)

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.

- **~7 Γ— sleep_delay seconds per character** (sequential)
- Slowest mode but works on empty databases

---

## Performance

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

### Example Timings (Boolean, 8 threads)

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

---

## Legal Disclaimer

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

---

## Credits

- Vulnerability chain discovered by **Adam Kues** (Assetnote / Searchlight Cyber)
- UNION fake-post technique: **sergiointel/wp2shell-poc** (originator)
- Implementation adapted from: **Icex0/wp2shell-poc**
- Route confusion detection technique: **Hadrian / Icex0**

---

## License

This tool is provided for educational and authorized testing purposes only.