
Chaîne RCE nginx indépendante de l'ASLR (PoC) combinant la fuite par surlecture de tas PoolSlip (CVE-2026-9256) avec le débordement rift (CVE-2026-42945) pour atteindre system() sur un nginx standard.
Première chaîne publique combinant deux bugs récemment divulgués du moteur de réécriture nginx en un seul
system() distant indépendant de l'ASLR sur l'image Docker officielle nginx:1.30.0 stock
(Debian 13, glibc 2.41) — aucune adresse codée en dur, aucun redémarrage de nginx, ~90 % par worker frais.
$args → fuite de libc + tas en direct.is_args (octets URL-safe uniquement) → un écrasement
partiel de 2 octets d'un pointeur de nettoyage limit_conn → ngx_destroy_pool le parcourt → system(cmd).Les deux sont le même décalage à deux passes de is_args pointé vers deux sinks. Analyse technique complète : Writeup
Divulgation et sécurité
- Les deux CVE sont déjà corrigées et activement exploitées dans la nature. Mettez à jour vers nginx 1.30.2 (stable) ou 1.31.1 (mainline) — notez que
1.30.1corrige rift mais reste toujours vulnérable à PoolSlip, donc seul1.30.2ferme les deux. NGINX Plus : R36 P5 / R32 P7 / R37.0.1.1.- Ceci est publié à des fins d'éducation et de tests autorisés uniquement. Cela s'exécute contre le lab Docker local fourni — ne le pointez pas vers une infrastructure que vous ne possédez pas ou pour laquelle vous n'avez pas d'autorisation écrite de tester.
| CVE | surnom | vulnérable | corrigé (nginx OSS) |
|---|---|---|---|
| CVE-2026-42945 | rift | 0.6.27 – 1.30.0 | 1.30.1 / 1.31.0 |
| CVE-2026-9256 | PoolSlip | 0.1.17 – 1.30.1 + 1.31.0 | 1.30.2 / 1.31.1 |
| Fichier | Objectif |
|---|---|
nginx.conf | Une configuration crédible de passerelle API (la cible — limit_conn, une réécriture de migration v1→v2, un proxy d'upload, une page de recherche dégradée). Aucun réglage d'allocateur. |
run.sh | Monte le lab local : nginx:1.30.0 stock servant nginx.conf, plus un upstream lent, sur l'hôte :19322. |
exp_official.py | La chaîne complète : fuite PoolSlip → dérivation libc/tas → spray → groom → écrasement partiel rift → system(). |
exp_official.py), curl, Linux x86-64.run.sh utilise perl (déjà présent dans l'image nginx stock) pour l'upstream lent, donc aucun paquet supplémentaire, aucun fichier slow_backend, et aucune connexion internet nécessaire. Le binaire nginx lui-même reste stock.WRITEUP.md, faites docker exec puis apt-get install gdb file binutils, puis chargez gef.git clone https://github.com/y198nt/Nginx-chain-Rift-Poolslip
cd Nginx-chain-Rift-Poolslip
bash run.sh # stock nginx:1.30.0 on http://127.0.0.1:19322
python3 exp_official.py --cmd 'id > /tmp/rce_proof' # leak → overwrite → system()
docker exec nginx-rift-official cat /tmp/rce_proof # → uid=101(nginx) ...
exp_official.py est à usage unique (~90 % par worker frais) : il fuit une fois et tire une fois. Si le
fichier de preuve est vide, le groom a atterri à un slot près — relancez-le simplement.
docker rm -f nginx-rift-official # tear the lab down
rewrite ^/search/((.*))$ /lookup?$1$2 fait que la passe de copie définit
r->args.len au-delà du buffer ; la page /search dégradée reflète $args dans le tas adjacent.
Après un court échauffement, un pointeur libpcre-cluster (fixe 0xb0ad08 sous libc) et un pointeur
de tas se stabilisent à des offsets stables → libc_base et heap_base, rien de codé en dur.limit_conn déjà valide — les octets de poids fort aléatoires de l'ASLR restent intacts →
indépendant de l'ASLR.ngx_pool_cleanup_t{handler=&system, data=cmd, next=0} pulvérisé ; la fermeture de la victime déclenche le parcours de nettoyage de ngx_destroy_pool →
system(cmd).Voir Writeup pour la dérivation complète, les sessions gdb, et les impasses/pivots.
rewrite dont le remplacement contient ? et référence une capture
PCRE ($1/$2), ou un motif de capture imbriquée comme ^/x/((.*))$ → /y?$1$2. Auditez les configurations pour
ceux-ci et retravaillez le rewrite/set fautif jusqu'à la correction.$args reflété (ou toute variable dérivée de la requête renvoyée en écho) est ce qui transforme la sur-lecture
en fuite d'informations — ne reflétez pas les arguments de requête verbatim.%2B/+ dans le chemin URI (GET /search/+++…), 503 provenant d'une page
dégradée renvoyant un long $args, et tempêtes de SIGSEGV/respawn des workers.