
Cadena RCE de nginx independiente de ASLR PoC que combina la fuga de sobrelectura de heap PoolSlip (CVE-2026-9256) con el desbordamiento rift (CVE-2026-42945) para alcanzar system() en nginx estándar.
Primera cadena pública de dos bugs recientemente divulgados del motor de reescritura de nginx en un único
system() remoto independiente de ASLR sobre la imagen Docker oficial estándar nginx:1.30.0
(Debian 13, glibc 2.41) — sin direcciones hardcodeadas, sin reinicio de nginx, ~90 % por worker nuevo.
$args → filtra libc + heap en vivo.is_args (solo bytes seguros para URL) → una
sobrescritura parcial de 2 bytes de un puntero de limpieza de limit_conn → ngx_destroy_pool lo recorre → system(cmd).Ambos son el mismo desajuste de dos pasadas de is_args apuntado a dos sinks. Análisis técnico completo: Writeup
Divulgación y seguridad
- Ambas CVE están ya parcheadas y bajo explotación activa en el mundo real. Actualiza a nginx 1.30.2 (stable) o 1.31.1 (mainline) — ten en cuenta que
1.30.1corrige rift pero sigue siendo vulnerable a PoolSlip, así que solo1.30.2cierra ambas. NGINX Plus: R36 P5 / R32 P7 / R37.0.1.1.- Esto se publica solo con fines de educación y pruebas autorizadas. Se ejecuta contra el laboratorio Docker local incluido — no lo apuntes a infraestructura que no poseas o para la que no tengas permiso por escrito para probar.
| CVE | apodo | vulnerable | corregido (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 |
| Archivo | Propósito |
|---|---|
nginx.conf | Una configuración creíble de API-gateway (el objetivo — limit_conn, una reescritura de migración v1→v2, un proxy de subida, una página de búsqueda degradada). Sin ajuste del asignador de memoria. |
run.sh | Levanta el laboratorio local: nginx:1.30.0 estándar sirviendo nginx.conf, más un upstream lento, en el host :19322. |
exp_official.py | La cadena completa: fuga de PoolSlip → derivar libc/heap → spray → groom → sobrescritura parcial de rift → system(). |
exp_official.py), curl, Linux x86-64.run.sh usa perl (ya presente en la imagen estándar de nginx) para el upstream lento, así que no hay paquete extra, ni archivo slow_backend, ni necesidad de internet. El binario de nginx en sí permanece estándar.WRITEUP.md, entra con docker exec e instala apt-get install gdb file binutils, luego carga 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 es de un solo intento (~90 % por worker nuevo): filtra una vez y dispara una vez. Si el
archivo de prueba está vacío, el groom cayó a un slot de distancia — simplemente vuelve a ejecutarlo.
docker rm -f nginx-rift-official # tear the lab down
rewrite ^/search/((.*))$ /lookup?$1$2 hace que la pasada de copia establezca
r->args.len más allá del búfer; la página degradada /search refleja $args en el heap adyacente.
Tras un breve calentamiento, un puntero de libpcre-cluster (fijo 0xb0ad08 por debajo de libc) y un puntero
de heap se asientan en offsets estables → libc_base y heap_base, nada hardcodeado.limit_conn ya válido — los bytes altos aleatorizados por ASLR viajan intactos →
independiente de ASLR.ngx_pool_cleanup_t{handler=&system, data=cmd, next=0} rociado; cerrar la víctima dispara el recorrido de limpieza de ngx_destroy_pool →
system(cmd).Consulta Writeup para la derivación completa, sesiones de gdb y los callejones sin salida/pivotes.
rewrite cuyo reemplazo contiene ? y referencia una captura
PCRE ($1/$2), o un patrón de captura anidada como ^/x/((.*))$ → /y?$1$2. Audita las configuraciones en busca de
esos y reelabora el rewrite/set problemático hasta que esté parcheado.$args reflejado (o cualquier variable derivada de la petición devuelta en la respuesta) es lo que convierte la sobrelectura
en una fuga de información — no reflejes los argumentos de la petición textualmente.%2B/+ en la ruta URI (GET /search/+++…), 503s de una página degradada
que devuelve un $args largo, y tormentas de SIGSEGV/respawn de workers.