## https://sploitus.com/exploit?id=C65E8753-E53D-588C-9EB7-3F4C50AF9469
# CVE-2026-63030 β wp2shell Lab
> **Pre-Authentication RCE in WordPress Core via REST API Batch Route Confusion + SQL Injection**
[](https://wordpress.org)
[](https://nvd.nist.gov/vuln/detail/CVE-2026-63030)
[](https://nvd.nist.gov)
[](LICENSE)
---
## Overview
**wp2shell** is a chain of two independently low-severity bugs in WordPress core that, when combined, allow an unauthenticated remote attacker to:
1. Reach an SQL injection sink with no credentials
2. Extract the admin password hash from the database
3. Create a new administrator account
4. Upload a webshell and achieve full Remote Code Execution
| Property | Detail |
|----------|--------|
| **CVEs** | CVE-2026-63030 + CVE-2026-60137 |
| **CVSS** | 9.8 Critical |
| **Auth required** | None (pre-authentication) |
| **Attack vector** | Network |
| **Affected versions** | WordPress 6.9.0β6.9.4 and 7.0.0β7.0.1 |
| **Patched versions** | 6.9.5 and 7.0.2 (released July 17, 2026) |
---
## The Two Bugs
### CVE-2026-63030 β REST API Batch Route Confusion
**File:** `wp-includes/rest-api/class-wp-rest-server.php`
`serve_batch_request_v1()` maintains two parallel arrays: `$matches[]` for handlers and `$validation[]` for results. When a sub-request fails with a `WP_Error` (broken path), it is pushed into `$validation[]` but **not** into `$matches[]`. This creates a +1 index shift β sub-request `i` gets dispatched with the handler for sub-request `i+1`.
```php
// VULNERABLE (7.0.1)
if ( is_wp_error( $route ) ) {
$responses[] = envelope();
continue; // $matches[] NOT pushed β BUG
}
// PATCHED (7.0.2)
if ( is_wp_error( $route ) ) {
$matches[] = null; // β FIX: keeps arrays in sync
$responses[] = envelope();
continue;
}
```
### CVE-2026-60137 β SQL Injection in WP_Query
**File:** `wp-includes/class-wp-query.php`
The `author__not_in` parameter expects an array of integers. When passed a string, `implode()` concatenates the raw value directly into the SQL `WHERE` clause β no escaping, no parameterization.
```php
// VULNERABLE (7.0.1)
$where .= ' NOT IN (' . implode(',', $q['author__not_in']) . ')';
// PATCHED (7.0.2)
$safe = implode(',', array_map('absint', (array) $q['author__not_in']));
$where .= " NOT IN ($safe)";
```
---
## Attack Chain
```
Unauthenticated Attacker
β
βΌ
POST /?rest_route=/batch/v1 β Outer batch
sub-req 0: "///" β WP_Error β index shift (+1)
sub-req 1: POST /wp/v2/posts β dispatched under BATCH handler
sub-req 2: POST /batch/v1 β dummy
β
β [Confusion #1 active]
βΌ
Inner batch (body of sub-req 1) β schema never validated
inner 0: "///" β index shift (+1)
inner 1: GET /wp/v2/posts?author_exclude=
dispatched under posts get_items()
β
β [Confusion #2 active]
βΌ
WP_Query: author__not_in = raw string
β
βΌ
SQL: NOT IN (0) UNION SELECT 999999,...,HEX(user_pass),...
β
βΌ
title.rendered = "||1|admin|$wp$2y$10$......||"
β
βΌ
Crack hash OR crack-free oEmbed technique
β
βΌ
POST /wp/v2/users β new admin β plugin upload β webshell β RCE
```
---
## Lab Setup
### Prerequisites
| Tool | Download |
|------|----------|
| **Docker Desktop** (Windows / macOS) | https://www.docker.com/products/docker-desktop |
| **Docker Engine** (Linux) | https://docs.docker.com/engine/install |
| **Git** | https://git-scm.com/downloads |
| **Burp Suite Community** (optional) | https://portswigger.net/burp/communitydownload |
---
### Step 1 β Clone the repository
```bash
git clone https://github.com/YOUR_USERNAME/cve-2026-63030-lab
cd cve-2026-63030-lab
```
You will see these files:
```
cve-2026-63030-lab/
βββ docker-compose.yml β defines WordPress + MySQL containers
βββ Dockerfile β custom image with Apache fix + wp-cli
βββ init.sh β configures permalink after install
βββ fix-htaccess.ps1 β Windows helper (run if Apache returns 404)
```
---
### Step 2 β Build and start the lab
```bash
docker compose up -d --build
```
This will:
- Build the custom WordPress 7.0.1 image (takes ~1β2 min on first run)
- Start a MySQL 8.0 database container
- Expose WordPress on **http://localhost:9090**
Verify both containers are running:
```bash
docker compose ps
```
Expected output:
```
NAME STATUS
wp2shell-lab running
wp2shell-db running
```
---
### Step 3 β Complete WordPress installation
Open **http://localhost:9090** in your browser and fill in:
| Field | Suggested value |
|-------|----------------|
| Site Title | `CVE-2026-63030` |
| Username | `admin` |
| Password | any password |
| Email | `admin@lab.local` |
Click **Install WordPress**, then log in.
---
### Step 4 β Enable REST API routing (required)
Run this once after installation:
**Linux / macOS:**
```bash
docker exec wp2shell-lab bash -c "
wp rewrite structure '/%postname%/' --allow-root --path=/var/www/html &&
wp rewrite flush --allow-root --path=/var/www/html
"
```
**Windows PowerShell:**
```powershell
docker exec wp2shell-lab bash -c "wp rewrite structure '/%postname%/' --allow-root --path=/var/www/html && wp rewrite flush --allow-root --path=/var/www/html"
```
Expected output:
```
Success: Rewrite structure set.
Success: Rewrite rules flushed.
```
---
### Step 5 β Fix .htaccess (Windows only, if you get 404 on REST API)
```powershell
.\fix-htaccess.ps1
```
---
### Step 6 β Verify the lab is ready
```bash
curl -s http://localhost:9090/wp-json/ | python3 -m json.tool | head -5
```
If you see a JSON response with `"namespaces"` β the lab is ready.
---
### Teardown
```bash
# Stop and remove everything including database
docker compose down -v
```
---
## Exploitation (PoC)
> β οΈ **For authorized security research and education only.**
> Only use against systems you own or have explicit written permission to test.
The full exploitation chain (detection β SQLi β admin creation β webshell β RCE) is implemented in:
**[github.com/Icex0/wp2shell-poc](https://github.com/Icex0/wp2shell-poc)**
```bash
git clone https://github.com/Icex0/wp2shell-poc
cd wp2shell-poc
pip install -r requirements.txt
# Step 1: Detection only (non-destructive)
python wp2shell.py check http://localhost:9090
# Step 2: Read database β extract users and hashes
python wp2shell.py read --preset users http://localhost:9090
```
---
## The Patch
Three files, fewer than 10 lines of PHP:
| File | Change |
|------|--------|
| `class-wp-rest-server.php` | `$matches[] = null` placeholder to keep arrays in sync |
| `class-wp-query.php` | `(array)` cast + `array_map('absint', ...)` |
| `class-wp-rest-posts-controller.php` | Same sanitization at the REST layer |
Update to **WordPress 6.9.5** or **7.0.2** to remediate.
---
## References
| Resource | Link |
|----------|------|
| GitHub Advisory (CVE-2026-63030) | `GHSA-ff9f-jf42-662q` |
| GitHub Advisory (CVE-2026-60137) | `GHSA-fpp7-x2x2-2mjf` |
| Public PoC | https://github.com/Icex0/wp2shell-poc |
---
Made with β€οΈ by **[Black Security Team](https://blacksecurityteam.com/)**
[](https://blacksecurityteam.com/)
[](https://t.me/Black_Security)
[](https://www.linkedin.com/company/black-security-team/)
> This repository is for **educational purposes and authorized security research only.**
> Do not test against systems you do not own or have explicit written permission to test.