
CVE-2026-63030
Proof-of-concept per un'iniezione SQL non autenticata nel core di WordPress che porta all'esecuzione remota di codice, tramite una route confusion del batch REST (CVE-2026-63030).
L'exploit non richiede credenziali né altra configurazione oltre a un target raggiungibile. Raggiunge l'iniezione tramite un singolo endpoint: POST /wp-json/batch/v1.
La scoperta in sé è un'iniezione SQL non autenticata: check la conferma e read dimostra la lettura arbitraria del database. Il comando shell è post-exploitation opzionale (credenziali amministrative recuperate → upload di un plugin → esecuzione di comandi), incluso per dimostrare l'impatto completo; non è la vulnerabilità.
| Branch | Affected | Fixed in |
|---|
| 6.9.x | 6.9.0 – 6.9.4 | 6.9.5 |
| 7.0.x | 7.0.0 – 7.0.1 | 7.0.2 |
Le versioni precedenti alla 6.9.0 non sono interessate da questa catena.
Il batch endpoint REST (/batch/v1) non è autenticato ed esegue più sub-request in una singola chiamata, affidandosi al fatto che ogni sub-request venga validata e controllata nei permessi singolarmente.
serve_batch_request_v1() costruisce due array paralleli — $matches (l'handler associato a ogni sub-request) e $validation (il risultato della validazione per ogni sub-request) — poi indicizza entrambi con lo stesso offset durante l'invio. Una sub-request il cui percorso fallisce wp_parse_url() viene aggiunta a $validation ma non a $matches, quindi gli array si disallineano e una sub-request viene inviata tramite l'handler di una diversa sub-request. Questa è la route confusion.
La PoC annida la primitiva due volte:
POST /wp/v2/posts con un corpo requests viene inviata tramite l'handler del batch stesso. Essendo stata validata come richiesta di posts, la sua lista requests non viene mai verificata contro lo schema del batch, quindi le sue sub-request possono usare GET: la allow-list dei metodi viene aggirata.GET /wp/v2/users con una stringa author_exclude (lo schema degli utenti non ha tale parametro, quindi il valore supera la validazione senza modifiche) viene inviata tramite get_items() dei posts. Lì author_exclude viene mappata sulla query var author__not_in di WP_Query, che la build vulnerabile interpola in SQL come stringa.Il risultato è un'iniezione SQL cieca basata su boolean e temporale raggiungibile in fase pre-autenticazione. Con l'accesso in lettura al database è possibile recuperare l'hash della password dell'amministratore; una volta crackato, l'accesso amministrativo consente l'esecuzione di codice tramite l'upload di un plugin.
Python 3.8+ e libreria standard. Nessuna dipendenza di terze parti.
Eseguilo dalla directory del repository:
./wp2shell.py <command> <url> [options]
Oppure pip install . per avere un comando wp2shell nel tuo PATH.
Conferma la sfruttabilità con un ritardo temporale differenziale. Non legge dati e non modifica nulla.
./wp2shell.py check http://target
./wp2shell.py read http://target # server fingerprint
./wp2shell.py read http://target --preset users # user logins and password hashes
./wp2shell.py read http://target --query "SELECT @@version"
Post-exploitation opzionale. Richiede credenziali amministrative valide; l'iniezione recupera l'hash della password, quindi fornisci qui il testo in chiaro recuperato.
./wp2shell.py shell http://target --user admin --password '<recovered>' --cmd id
./wp2shell.py shell http://target --user admin --password '<recovered>' -i # interactive shell
shell carica una webshell come plugin (protetta da un percorso casuale e da un token per esecuzione) e ne stampa il percorso. Rimuovila al termine.
| Option | Applies to | Description |
|---|---|---|
--rest-route | all | Usa /?rest_route=/batch/v1 (per siti senza permalink personalizzati). |
--proxy URL | all | Instrada il traffico attraverso un proxy HTTP (ad esempio, Burp). |
--timeout N | all | Timeout delle richieste in secondi. |
--sleep N | check | Ritardo usato per confermare l'iniezione. |
--preset | read | fingerprint o users. |
--query | read | Un'espressione SQL scalare da leggere. |
--prefix | read | Prefisso delle tabelle del database (default wp_). |
--max-length N | read | Numero massimo di caratteri letti per valore (default 128). |
--user / --password | shell | Credenziali amministrative (in chiaro, recuperate dall'hash). |
--cmd | shell | Comando da eseguire (omettilo quando usi -i). |
-i / --interactive | shell | Apre una shell interattiva dopo il deploy. |
Aggiorna a WordPress 6.9.5 o 7.0.2. Nel frattempo, blocca sia /wp-json/batch/v1 sia il parametro della query rest_route=/batch/v1 al perimetro, oppure richiedi l'autenticazione per il batch endpoint tramite il filtro rest_pre_dispatch.
Solo per test di sicurezza autorizzati. Usalo esclusivamente su sistemi di tua proprietà o per i quali hai esplicita autorizzazione scritta al test. L'autore non si assume alcuna responsabilità per un uso improprio.