
Rileva il pattern di rewrite CVE-2026-42945 nelle configurazioni nginx: rewrite con ? nella sostituzione più una cattura senza nome consumata nella stessa location.
Trova configurazioni vulnerabili a CVE-2026-42945, l'heap buffer overflow nel ngx_http_rewrite_module di nginx.
Eseguire una versione di nginx affetta (dalla 0.6.27 alla 1.30.0) non basta perché la vulnerabilità sia sfruttabile. La configurazione deve contenere una coppia specifica di direttive, e quella coppia è rara: due scan indipendenti su configurazioni reali hanno trovato zero configurazioni sfruttabili su 1465 e una su 35633. Questo script ti dice da quale parte di quella linea ti trovi.
Una direttiva rewrite la cui sostituzione contiene un ? seguito da argomenti, seguita da una cattura senza nome ($1–$9) letta nello stesso location:
location / {
rewrite ^(.*) /new?c=1; # the ? here raises the internal is_args flag
set $myvar $1; # ...which was never cleared, and leaks into this
return 200 $myvar;
}
Un passaggio calcola la dimensione del buffer per la stringa grezza, il successivo vi copia una versione con escape. Le due metà, prese singolarmente, sembrano corrette.
Ogni regola seguente è stata misurata su nginx 1.30.0 anziché dedotta, perché le descrizioni pubblicate ne sbagliano due.
Due conseguenze che vale la pena esplicitare:
Un ? finale non è pericoloso. /new/$1? è il modo per eliminare la query string ereditata, e compare in una larga parte delle configurazioni di migrazione. Segnalare ogni ? metterebbe in allarme metà di internet.
"Le catture con nome sono sicure" è un'affermazione errata. Ciò che conta è come la cattura viene letta, non come è stata dichiarata. Un gruppo con nome letto come $1 va in crash esattamente come uno senza nome.
Vengono inoltre ignorati, per le stesse ragioni misurate: un ? dentro la regex stessa (un quantificatore), e redirect / permanent / break, che terminano l'elaborazione prima che il consumatore venga eseguito.
nginx -T | python3 check_rewrite.py
python3 check_rewrite.py /path/to/dump.txt
python3 check_rewrite.py --json /path/to/dump.txt
Python 3, solo libreria standard.
Codici di uscita: 0 pulito, 1 trovata una coppia, 2 la configurazione non può essere letta o analizzata. Il terzo è il punto: una virgoletta non chiusa o delle parentesi graffe sbilanciate troncano l'analisi, e un checker che risponde "nessun risultato" per un file che non è riuscito a leggere è peggiore di uno che va in crash. Quando succede, lo dice e restituisce 2.
Passagli nginx -T, non singoli file: gli include possono trovarsi ovunque, e una configurazione generata è vera solo sul server in esecuzione. I risultati sono riportati rispetto al file e alla riga in cui la direttiva si trova realmente, non all'offset nel dump.
$ python3 check_rewrite.py samples/vuln-nginx-T.txt
[HIGH] находка #1
location: location / (/etc/nginx/nginx.conf:14)
rewrite: rewrite ^(.*) /new?c=1 (/etc/nginx/nginx.conf:15)
захват $N: set -> set $myvar $1 (/etc/nginx/nginx.conf:16)
почему: обработка продолжается в этом же location без редиректа
Итого находок: 1
Stampati anche alla fine di ogni report, così nessuno scambia lo strumento per una garanzia:
location;rewrite e set posizionate direttamente in server anziché dentro un location non vengono tracciate;set $tmp $1; e successivo uso di $tmp) non vengono seguite;try_files, error_page e location nominati non vengono seguiti;if che non scatta mai viene comunque riportata.L'aggiornamento è più affidabile di qualunque controllo di configurazione. Prendi l'ultima release stabile o mainline da nginx.org.
python3 -m unittest test_check_rewrite -v
29 test. La maggior parte sono casi negativi, perché il rischio in uno strumento del genere è gridare contro configurazioni che sono a posto. È stato inoltre eseguito contro una configurazione di produzione di 250 righe distribuite su undici file inclusi, con una regex in map e un header CSP pieno di punto e virgola dentro virgolette: zero risultati, zero lamentele di parsing, e esattamente un risultato dopo aver iniettato una coppia reale.
MIT
| Configurazione in un location | Crash |
|---|
rewrite ^(.*) /new?c=1; + set $x $1; | sì |
rewrite ^/old/(.*)$ /new?x=1; + set $x $1; | sì |
rewrite ^/old/(.*)$ /new/$1?; + set $x $1; | no |
rewrite ... /mid?c=1; then rewrite ... /new; then set $x $1; | no |
rewrite ^(.*) /new?c=1 break; + set $x $1; | no |
rewrite ^(?<tail>.*) /new?c=1; + set $x $1; | sì |
rewrite ^(?<tail>.*) /new?c=1; + set $x $tail; | no |