
Analisi e implementazione end-to-end della vulnerabilità RCE di WordPress patchata - CVE-2026-60137 e CVE-2026-63030

Tutti i PoC sfruttano gli stessi due bug - la confusione di route REST batch (CVE-2026-63030) e l'iniezione SQL author__not_in (CVE-2026-60137) - con la stessa forma di batch doppiamente annidato. Ciò che differisce tra loro è il percorso RCE scelto, le precondizioni di ambiente e le impostazioni di sicurezza predefinite. Questo documento dichiara, concretamente, dove si colloca l'implementazione in questo repository in quel panorama.
check non invia alcun payload SQL se non richiesto; tutto il traffico può portare un tag di attribuzione; tutto ciò che il comando shell scrive sul target viene rimosso automaticamente in seguito. Vedi §3.| Capacità | Questo repository | Icex0/wp2shell-poc | sergiointel/wp2shell-poc | 0xsha/wp2shell | Variante OUTFILE [4] |
|---|---|---|---|---|---|
| Lettura SQLi blind/timing pre-autenticazione | sì | sì | sì (timing) | sì | sì |
| Lettura UNION in-band (1 richiesta/valore) | sì | sì | - [1] | - [1] | - |
| Lettura basata su errori (EXTRACTVALUE) | sì | sì | - | - | - |
| Il canale UNION sopravvive alla cache degli oggetti persistente | sì (killed-base) | no - la sonda dà falsi negativi [2] | non documentato | no - precondizione documentata [3] | n/a [5] |
| RCE pre-autenticazione senza crack | sì (ponte SQLi→admin) | sì (stesso ponte) | sì (origine del ponte) | sì (stesso ponte) | sì, tramite INTO OUTFILE [5] |
| Precondizioni aggiuntive per la RCE | nessuna oltre all'installazione predefinita | nessuna (su host senza cache degli oggetti) | nessuna (idem) | nessuna (idem) | privilegio FILE MySQL + percorso scrivibile dal web condiviso con mysqld |
| Check non distruttivo / validazione della patch | sì (tripletta di marker; nessun payload di default) | sì | no | sì (block_cannot_read) | sì (batch di marker) |
| Tag di attribuzione/User-Agent | sì, su tutti i comandi | no | no | flag di trasporto | no |
| Pulizia automatica (webshell + admin generato) | sì | sì | non documentato | solo webshell protetta da token | dropper rimosso [5] |
| Guida al rilevamento per il blue team | sì, da un'esecuzione in produzione | no | no | matrice di laboratorio invece | note di mitigazione |
| Dipendenze | solo stdlib | solo stdlib | file singolo | solo stdlib, file singolo | pacchetto Python ≥3.10 |
[1] Solo timing/blind come canale di lettura; la primitiva UNION del finto post esiste all'interno del ponte ma non è esposta come oracolo di estrazione.
[2] La sonda di disponibilità ingenua (0) UNION SELECT …) viene silenziosamente scartata durante l'idratazione della cache degli oggetti, available() restituisce false e l'intero ponte pre-autenticazione si interrompe - vedi §1.
[3] Il README del progetto stesso elenca "nessuna cache degli oggetti persistente (Redis/Memcached)" tra le Precondizioni.
[4] Variante pubblica rispecchiata su Sploitus (link sotto): lettura blind più un dropper INTO OUTFILE come passo RCE, usando un carrier di categorie per_page=-1.
[5] Il percorso RCE OUTFILE non dipende dal rendering del finto post, quindi le cache degli oggetti non lo bloccano - a bloccarlo sono il privilegio MySQL FILE e una directory scrivibile condivisa. L'hosting gestito non concede quasi mai FILE all'utente DB di WordPress, e secure_file_priv è comunemente impostato.
La primitiva UNION del finto post dipende da come WP_Query restituisce le righe:
wp_posts; una riga iniettata con UNION diventa direttamente un WP_Post. Il falso viene renderizzato.Su host con cache degli oggetti persistente, un set di base popolato spinge WP_Query in modalità suddivisa. La sonda standard usata dai PoC pubblici -
0) UNION SELECT <forged row> -- -
post_author NOT IN (0) corrisponde a ogni riga), quindi dietro una cache degli oggetti la riga falsificata evapora: la sonda di disponibilità dà un falso negativo, available() restituisce false e l'intero ponte pre-autenticazione viene segnalato come "morto" su un host che in realtà è pienamente sfruttabile. L'unificatore pubblico documenta lo stesso confine elencando "nessuna cache degli oggetti persistente" come precondizione rigida.Questo repository invece svuota il set di base:
1) AND 1=0 UNION ALL SELECT <forged row> -- -
Con zero righe di base, la riga falsificata è l'unica riga; la query resta in modalità riga intera; nessuna lookup di idratazione viene mai eseguita. Una singola parola chiave iniettata (AND 1=0) è l'intera differenza tra "canale UNION morto" e "RCE pre-autenticazione completa" su host con cache degli oggetti - che sono la maggioranza degli ambienti di produzione WordPress gestiti. La diagnosi, la matrice di sondaggio (per_page × forma di iniezione).
Nota sull'ambito: il canale di lettura blind/timing non è sensibile alla cache degli oggetti (contare le righe in SQL non coinvolge l'idratazione del finto post), quindi la lettura blind di ogni PoC funziona ovunque. Ciò che la cache degli oggetti uccide negli altri PoC è specificamente la parte dipendente da UNION: l'estrazione in-band e il ponte SQLi→admin.
Una seconda lezione correlata documentata nel case study: quando entrambi i canali funzionano, considera la lettura UNION in-band come autorevole - l'oracolo timing in produzione ha prodotto bit flippati in condizioni di jitter su un valore che la lettura in-band ha risolto senza ambiguità.
Esistono tre percorsi RCE pre-autenticazione nei PoC pubblici:
| Percorso | Usato da | Precondizioni aggiuntive |
|---|---|---|
Ponte SQLi→admin (forgiare righe oEmbed/changeset/nav → POST /wp/v2/users → login → upload plugin) | questo repository, sergiointel (origine), Icex0, 0xsha | nessuna oltre a un'installazione predefinita |
Dropper INTO OUTFILE (scrive un file PHP tramite SQLi, lo recupera per ottenere una shell) | variante OUTFILE [4] | privilegio MySQL FILE, secure_file_priv che lo consente e una directory scrivibile da mysqld e servita dal web server |
Recupero hash → crack → login (dump di user_pass, crack offline, poi upload del plugin) | tutti (come fallback) | l'hash bcrypt deve effettivamente craccarsi ($wp$2y$, hashcat -m 35500) - lento, spesso mai |
Questo repository implementa il ponte: non richiede privilegi di database oltre a quelli che WordPress ha già, funziona quando i livelli DB e web non condividono nulla e non lascia alcun file dietro di sé su cui il percorso basato sul privilegio FILE possa dipendere. Il compromesso è la complessità - il ponte è un grafo di post avvelenati di sette righe - che è esattamente il punto in cui il falso negativo della cache degli oggetti di §1 nascondeva il percorso.
Progettato per essere eseguito contro sistemi in produzione sotto autorizzazione, non solo laboratori:
check è non distruttivo di default - fingerprint passiva più un batch benigno di marker; nessun payload SQL viene inviato a meno che non venga fornito --confirm-sqli. Dopo la patch, la tripletta di marker che scompare funge anche da validazione della correzione.--user-agent su ogni comando così che tutto il traffico di exploit sia identificabile nei log (una regola pratica degli engagement che gli strumenti pubblici non applicano di default).author_exclude → author__not_in, la primitiva UNION del finto WP_Post e il concetto di customizer-bridge sono tutte tecniche pubbliche (discendenza riconosciuta sotto).INTO OUTFILE), rispecchiata su Sploitus - https://sploitus.com/exploit?id=7CD079AD-E27B-5C54-A696-60635BFDB241