
Toolkit di rilevamento e laboratorio riproducibile per CVE-2026-87902, un path traversal non autenticato in WordPress. Include un checker remoto, un analizzatore di IoC, regole Sigma e un banco di prova Docker.
check/ | Controllore remoto, passivo, senza accesso al server |
detect/ | Analizzatore di IoC + regole Sigma |
offensive/ | Generatore di tracce, per validare il rilevamento su log reali |
docker-compose.yml + provision/ | Banco di prova, tre configurazioni |
tests/validate.py | Cancello di qualità — condiziona ogni pubblicazione |
docs/ANALYSIS.md | La vulnerabilità, la correzione, l'analisi di raggiungibilità misurata |
make up # monta il banco make ioc # corpus d'attacco -> log -> rilevamento
make scan # controlla il banco make test # cancello di qualità
Tre istanze WordPress su 127.0.0.1, che isolano ogni fattore del verdetto.
| Porta | Istanza | Core | Tema attivo | Verdetto atteso |
|---|---|---|---|---|
| 8091 | vuln-pre | 6.8.1 — non corretto | Twenty Twelve, con page-templates/ | AFFECTED_PRECONDITION_MET |
| 8092 | vuln-nopre | 6.8.1 — non corretto | Twenty Twenty-Four, senza page-* | AFFECTED_CORE_ONLY |
| 8093 | patched | 6.8.10 — corretto | Twenty Twelve, con page-templates/ | NOT_AFFECTED |
8092 è la più istruttiva : stesso core vulnerabile di 8091, ma manca il prerequisito
del tema. È ciò che dimostra che un triage basato sulla sola versione sovrastima
l'esposizione. Il tema è l'unica variabile tra 8091 e 8092, la correzione
l'unica tra 8091 e 8093 : tutte e tre includono lo stesso tipo di contenuto
personalizzato (provision/mu-plugins/00-lab-cpt.php) e la stessa sonda di stato della
richiesta (10-lab-debug.php, header X-Lab-* che espongono is_page(), il valore di
pagename visto dal loader e il template infine incluso).
make up # avvia e provisiona — idempotente, riavviabile
make status # versione di ogni istanza
make down # arresto make clean : elimina anche i volumi
L'immagine Docker ufficiale fa puntare /var/log/apache2/access.log a
/dev/stdout : i log escono sullo stdout del container, non in un file.
docker compose logs --no-log-prefix vuln-pre # accessi + errori
docker compose logs --no-log-prefix vuln-pre > access.log # per analisi
docker compose logs -f --no-log-prefix vuln-pre # in diretta
make logs # le tre istanze
Su un server classico : /var/log/apache2/access.log, /var/log/nginx/access.log,
o /home/*/logs/ presso la maggior parte degli hosting condivisi. Il formato deve
includere la query string — %r o il formato combined la includono, un LogFormat
basato su %U la perde, e senza di essa nessun rilevamento è possibile.
check/wp-ghsa-7hp8-check.pyDa Internet, senza accesso al server. Nessun payload, nessun traversal, nessuna
scrittura. Per ogni host : rilevamento WordPress, versione incrociata su cinque fonti
(meta generator, feed RSS, wp-links-opml.php, readme.html, ?ver= delle
risorse del core), tema attivo, e sondaggio della directory page-*.
python3 check/wp-ghsa-7hp8-check.py --hosts-file hosts.txt --csv out.csv --json out.json
| Verdetto | Significato |
|---|---|
AFFECTED_PRECONDITION_MET | Core vulnerabile e directory page-* sul tema attivo. Prioritario. |
AFFECTED_THEME_UNKNOWN | Core vulnerabile, tema non determinato. |
AFFECTED_CORE_ONLY | Core vulnerabile, prerequisito tema assente. Da correggere comunque. |
VERSION_UNKNOWN | WordPress rilevato, versione mascherata. |
NOT_AFFECTED | Versione ≥ correzione del proprio branch. |
« AFFETTO » significa che il codice vulnerabile è presente, non che un attaccante
possa eseguire codice. Vedi docs/ANALYSIS.md.
--transport browser (predefinito) pilota il Google Chrome installato ; le sotto-richieste
partono da un fetch() eseguito dentro la pagina ed ereditano il suo stack TLS, il suo
ordine di header HTTP/2 e i suoi cookie, il che evita di essere filtrati da un CDN prima
che l'URL venga letto. --transport direct utilizza solo la libreria standard.
--scheme http|https evita il fallback https → http, che altrimenti lascia una riga 400
con un ClientHello TLS grezzo nel log del target.
Ogni richiesta è timestampata al millisecondo in un log JSONL : identificatore di
sessione, numero di richiesta, fase, URL, stato, dimensione, durata, IP di uscita, marker.
Il marker SECAUDIT/<nonce> parte come header X-Security-Audit e come suffisso
dell'User-Agent — aggiunto, mai sostituito, per restare visibile in un access log
standard senza rompere la firma del browser. Personalizzabile tramite --marker.
Nessun oracolo remoto. L'opzione
--probe-inclusioneffettua un confronto differenziale su target inerte (wp-includes/version.php, già caricato al bootstrap :require_oncene farebbe un no-op integrale). Su un'installazione standard restituisceNOT_REACHABLEanche su un core vulnerabile, con risposte identiche all'ottetto tra istanza corretta e non corretta. Non è un limite dello strumento : WordPress risponde 404 prima di consultare la gerarchia dei template. Dimostrazione numerica indocs/ANALYSIS.md, sezione 3.
Una regex letterale del tipo pagename=.*%2e%2e%2f si aggira cambiando il case
(%2E), codificando una volta in più (%252e), o mescolando letterale e codificato
(templates/..%2f../). Qualsiasi lista di pattern è incompleta per costruzione.
Si parte quindi dal codice, non dalla scrittura dell'attaccante :
pagename subisce al massimo due decodifiche prima di raggiungere il disco — quella di
PHP sulla query string, poi l'urldecode() esplicito di get_page_template().file_exists() deve contenere
un componente ... Sotto Linux, la directory padre si scrive esattamente sui due
byte 0x2E 0x2E ; non esiste nessun'altra rappresentazione a livello del
filesystem.Quindi : decodificare fino al punto fisso e testare a ogni livello. È un sovrainsieme stretto di ciò che fa WordPress — aggiungere uno strato di codifica non fa che spostare la corrispondenza di un livello, che attraversiamo comunque.
python3 detect/wp-ghsa-7hp8-ioc.py access.log
docker compose logs --no-log-prefix vuln-pre | python3 detect/wp-ghsa-7hp8-ioc.py -
| Regola | Severità | Trigger |
|---|---|---|
GHSA-7hp8-traversal-pagename | CRITICAL | componente .. in pagename, a qualsiasi livello di decodifica |
GHSA-7hp8-traversal-param | HIGH | stessa primitiva in un altro parametro (temi ed estensioni chiamano anche locate_template()) |
GHSA-7hp8-traversal-path | HIGH | componente .. nel percorso URL (nginx lascia passare %2f, Apache no per impostazione predefinita) |
GHSA-7hp8-overlong-encoding | MEDIUM | sovra-codifica UTF-8 (%c0%ae). Inoperante sotto Linux, ma mai legittimo |
GHSA-7hp8-theme-page-dir-probe | LOW | sondaggio di una directory page-* del tema — la ricognizione |
Regole Sigma in detect/sigma-wp-ghsa-7hp8.yml.
Sigma non sapendo decodificare ricorsivamente, esse enumerano i livelli da 0 a 3 : è
un'approssimazione assunta, per il triage di primo livello. Ripassare le
corrispondenze nell'analizzatore per decidere.
WP::parse_request() legge $_POST prima di $_GET. pagename può quindi
arrivare in un corpo di richiesta, assente da qualsiasi access log. Copertura necessaria a
livello WAF o ModSecurity, sul corpo.Nessuna regola su access log copre il primo punto. È un limite del supporto, non della regola — ma deve essere noto prima di annunciare una copertura completa.
offensive/generate-traces.py riproduce 12 diverse scritture dello stesso payload
(letterale, codifica singola/doppia/tripla, case alto e basso, mescolanze,
sovra-codifica UTF-8, punto-spazio) più 7 richieste legittime che vi assomigliano
(slug con punti, wp-includes in uno slug, permalink con data, percento codificato).
Non ottiene nulla : non esiste un exploit remoto per questo vettore su un'installazione standard. Produce tracce — è tutto il suo scopo.
make ioc # genera il corpus, recupera i log reali, passa l'analizzatore
Atteso, e verificato da make test su log Apache reali :
12 payload rilevati su 12 (11 CRITICAL, la sovra-codifica in MEDIUM perché
non è sfruttabile sotto Linux), 0 alert sulle 7 richieste legittime, e
rilevamento effettivo a tre livelli di decodifica diversi.
Target limitato al banco locale ; qualsiasi altro richiede --i-have-authorization.
register_argc_argv = Off nel php.ini del SAPI web ; disinstallare PEAR se
inutilizzato — è il pivot inclusione → esecuzione citato dall'avviso.open_basedir limitato alla radice del sito : confina qualsiasi inclusione locale.make test è la condizione di pubblicazione : matrice dei 25 branch dell'avviso
(inclusi i tranelli di confronto — 6.8.9 < 6.8.10 numericamente, pre-versioni,
branch fuori matrice), controllore contro le tre istanze, e regola IoC validata
su log reali. L'asserzione NOT_REACHABLE della sonda vi è fissata volontariamente :
se si rompe, è il comportamento che è cambiato e l'analisi deve essere ripresa.
Da utilizzare solo su asset di cui si è responsabili, o sotto mandato scritto.
Il banco ascolta solo su 127.0.0.1 ; le istanze vulnerabili non devono mai essere
esposte. La sonda 10-lab-debug.php divulga percorsi del server : è riservata
al banco.