
Analyse CVE-2026-42945, un dépassement de tas dans la réécriture NGINX, fournissant un détecteur différentiel, un PoC DoS déterministe et un port RCE crédité avec vérification.
Recherche en sécurité sur CVE-2026-42945 (CVSS 4.0 9.2 CRITIQUE) : un
dépassement de tampon sur le tas dans le moteur de scripts de NGINX où une
rewrite avec des arguments dans le remplacement (par ex. rewrite ^(.*) /new?c=1;)
laisse le drapeau is_args du moteur activé et jamais réinitialisé — de sorte que
toute capture ($1) évaluée ensuite dans set/if est copiée avec échappement
d'URI dans un tampon dimensionné pour la longueur sans échappement. Atteignable
sans authentification par tout client dont l'URI de requête contient des échappements
%XX ou des +.
Résultat (phase 1) : un détecteur différentiel sûr (version vulnérable
1.30.0 vs corrigée 1.30.1, zéro crash) plus un DoS déterministe du worker
(SIGSEGV, core dumped — la réponse est entièrement servie d'abord). Les deux
reproduits contre des builds compilés à partir des sources au commit 71d62c6.
Résultat (phase 2) : un port crédité de la RCE du rapporteur —
exécution de system() en tant qu'utilisateur worker (nobody), reproduite 3/3
exécutions propres contre le build épinglé du rapporteur répliqué sous lab/rce/.
La technique d'exploitation est celle de Leo Lin (DepthFirst) ; voir
Ce qui est original ici vs travaux antérieurs et la section Travaux antérieurs du rapport.
| Affirmation | Preuve |
|---|---|
| Cause racine dérivée du commit de correction (pas des blogs) | finding-sheet |
La correction est une ligne — e->is_args = 0; dans ngx_http_script_regex_end_code() | commit 524977e (cité dans writeup) |
| Détecteur sûr : oracle vulnérable vs corrigé, zéro crash | poc/diagnose.py — empreinte de troncature (/foo%25b vs /foo%bar) |
| Matrice de déclenchement : URI entre guillemets/plus et caractères échappables dans la capture — les deux requis | batterie de sondes poc/diagnose.py incl. le contrôle /a%41b |
| DoS déterministe du worker (SIGSEGV, core dumped, réponse servie d'abord) | poc/crash.py — le changement de X-Worker-PID prouve la mort du worker |
| La version corrigée 1.30.1 survit à l'attaque identique | poc/crash.py --host 127.0.0.1:29544 — 3/3 rounds survécus |
| RCE (phase 2, technique du rapporteur, port crédité) | exploit/rce.py — 3/3 exécutions, fichier de preuve + uid=65534(nobody), workers exited on signal 11 après exécution |
# paire différentielle : nginx 1.30.0 (:29543) + 1.30.1 (:29544) compilés à partir des sources
cd lab && docker compose up -d --build && cd ..
python3 poc/diagnose.py # oracle sûr — ne fait jamais crasher aucun worker
python3 poc/crash.py --host 127.0.0.1:29543 # 1.30.0 : le worker meurt sur signal 11, core dumped
python3 poc/crash.py --host 127.0.0.1:29544 # 1.30.1 : survit à chaque round
docker logs cve-42945-vuln 2>&1 | grep 'exited on signal' | tail -3
Phase 2 — RCE contre le build épinglé du rapporteur (rev dev nginx
98fc3bb7 = release-1.29.8-14, ASLR désactivé par le point d'entrée épinglé) :
docker compose -f lab/rce/docker-compose.yml up -d --build # :29545, minutes de compilation
python3 exploit/rce.py \
--cmd 'echo pwned-by-cve-2026-42945 > /tmp/proof' --verify --expect pwned
docker compose -f lab/rce/docker-compose.yml exec nginx cat /tmp/proof
| Doc | Ce que c'est |
|---|---|
| docs/finding-sheet.md | Fiche technique d'une page : versions, CVSS, motif de configuration, conditions de déclenchement, signatures de détection |
| docs/report-ptes.md | Rapport structuré PTES (7 phases), limité à ce qui a réellement été fait |
| docs/research-writeup.md | Le récit de l'investigation — archéologie du commit de correction, la surprise de la troncature, leçons portables, travaux futurs |
La chaîne de recherche de la phase 1 est un travail original : l'oracle
différentiel sans crash et son empreinte de troncature (le corps servi est la
capture ré-échappée tronquée à la longueur brute — la signature visible du
désalignement longueur/valeur en deux passes), la matrice de déclenchement prouvant
que les deux préconditions sont nécessaires, l'explication au niveau du code source
de pourquoi la passe de longueur ne peut pas voir is_args, et la mort
déterministe du worker après la réponse. Le bug lui-même, le motif de configuration
vulnérable et l'analyse du commit de correction sont des travaux antérieurs du
rapporteur et de l'amont — Leo Lin (DepthFirst) a divulgué le problème et Roman
Arutyunyan l'a corrigé ; voir la section Travaux antérieurs du rapport et
DepthFirst Disclosures — Nginx-Rift,
arborescence CVE-2026-42945.
La phase 2 (RCE) est un port de l'exploit publié du rapporteur, pas une
chaîne indépendante : le feng shui de tas inter-requêtes qui fait atterrir le
dépassement sur le pointeur cleanup du pool de requêtes adjacent, le spray de faux
ngx_pool_cleanup_t via des corps POST maintenus, la redirection de la
distribution cleanup vers system() de libc, la géométrie du débordement (349 A +
969 + + 6 octets cibles), les constantes de base/décalage sans ASLR, et le build
épinglé dans lab/rce/ (leur env/) sont tous la conception de Leo Lin, reproduite
avec crédit dans exploit/rce.py. Ce qui est à nous dans la phase 2 : le port
lui-même — les conventions CLI/journalisation/vérification de ce dépôt, la
découverte de candidats par analyse de tas en direct et l'autodétection de base
(leurs constantes statiques dépendent du noyau ; voir le docstring de l'exploit),
l'écouteur de reverse-shell de la bibliothèque standard, et les preuves de
vérification.
Ce dépôt partage sa méthodologie avec CVE-2026-42533 — Dépassement de tas pré-auth NGINX : analyse et RCE (dépôt frère) : la même classe de bug longueur/valeur en deux passes dans le moteur de scripts nginx, poussée là-bas jusqu'à une chaîne RCE complète pré-auth. Cette chaîne délimitait le plafond plausible pour ce bug ; la phase 2 de ce dépôt atteint le même plafond ici — en tant que port crédité de la technique du rapporteur, pas une chaîne indépendante.
docs/ fiche de découverte · rapport PTES · rapport de recherche
poc/ diagnose.py (oracle différentiel sûr) · crash.py (DoS déterministe)
exploit/ rce.py — port crédité de la chaîne system() du rapporteur
lab/ Dockerfile (nginx à partir des sources, -g -O0) + paire différentielle compose
lab/nginx/ vulnerable.conf — la config PoC du commit de correction
lab/rce/ environnement RCE épinglé du rapporteur (leur env/, notre mapping de port)
524977e7c534
"Rewrite: fixed escaping and possible buffer overrun" (Roman Arutyunyan) — publié dans nginx 1.30.1CVE-2026-42945 (travaux antérieurs du rapporteur ; Leo Lin)Artéfact de recherche pédagogique. À exécuter uniquement contre des systèmes que vous possédez ou pour lesquels vous êtes explicitement autorisé à tester. La vulnérabilité est corrigée — mettez à niveau nginx vers ≥ 1.30.1.