LFI2RCE via Nginx archivos temporales

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Configuración vulnerable

Example from bierbaumer.net mostró que incluso la siguiente one-liner es suficiente cuando PHP se ejecuta detrás de un reverse proxy nginx que almacena en búfer los cuerpos de las peticiones en disco:

<?php
$action = $_GET['action'] ?? 'read';
$path   = $_GET['file'] ?? 'index.php';
$action === 'read' ? readfile($path) : include $path;

El lado de nginx suele mantener rutas temporales por defecto como /var/lib/nginx/body y /var/lib/nginx/fastcgi. Cuando un cuerpo de petición o respuesta upstream es mayor que el buffer en memoria (≈8 KB por defecto), nginx escribe transparentemente los datos en un archivo temporal, mantiene el descriptor de archivo abierto y solo elimina el nombre del archivo (unlink). Cualquier PHP include que siga enlaces simbólicos (como /proc/<pid>/fd/<fd>) puede aún ejecutar el contenido unlinkeado, dándote RCE a través de LFI.

Por qué los archivos temporales de nginx son abusables

  • Los cuerpos de solicitud que exceden el umbral del buffer se vuelcan a client_body_temp_path (por defecto a /tmp/nginx/client-body o /var/lib/nginx/body).
  • El nombre de archivo es aleatorio, pero el descriptor de archivo sigue siendo accesible bajo /proc/<nginx_pid>/fd/<fd>. Mientras el cuerpo de la petición no se haya completado (o mantengas la conexión TCP colgando), nginx mantiene el descriptor abierto aunque la entrada de ruta haya sido eliminada (unlinked).
  • Los include/require de PHP resuelven esos enlaces simbólicos /proc/.../fd/..., así que un atacante con LFI puede saltar por procfs para ejecutar el archivo temporal en búfer incluso después de que nginx lo borre.

Flujo clásico de explotación (recapitulación)

  1. Enumera los PIDs de los workers. Obtén /proc/<pid>/cmdline vía LFI hasta encontrar cadenas como nginx: worker process. El número de workers rara vez supera el número de CPUs, así que solo necesitas escanear el espacio de PIDs bajos.
  2. Forzar a nginx a crear el archivo temporal. Envía bodies POST/PUT muy grandes (o respuestas proxied) para que nginx vuelque a /var/lib/nginx/body/XXXXXXXX. Asegúrate de que el backend nunca lea todo el body — p. ej., mantén viva la conexión durante la subida para que nginx deje el descriptor abierto.
  3. Mapear descriptores a archivos. Con la lista de PIDs, genera cadenas de traversal como /proc/<pidA>/cwd/proc/<pidB>/root/proc/<pidC>/fd/<fd> para evitar cualquier normalización de realpath() antes de que PHP resuelva el objetivo final /proc/<victim_pid>/fd/<interesting_fd>. Brute-forcear descriptores de archivo 10–45 suele ser suficiente porque nginx reutiliza ese rango para archivos temporales de body.
  4. Incluir para ejecución. Cuando das con el descriptor que todavía apunta al body en búfer, una sola llamada include o require ejecuta tu payload —aunque el nombre original del archivo ya haya sido unlinkeado. Si solo necesitas leer el archivo, usa readfile() para exfiltrar el contenido temporal en vez de ejecutarlo.

Variantes modernas (2024–2025)

Los ingress controllers y service meshes ahora exponen rutinariamente instancias de nginx con superficie de ataque adicional. CVE-2025-1974 (“IngressNightmare”) es un buen ejemplo de cómo evoluciona el truco clásico de archivos temporales:

  • Los atacantes suben un objeto compartido malicioso como cuerpo de petición. Debido a que el body es >8 KB, nginx lo bufferiza en /tmp/nginx/client-body/cfg-<random>. Mintiendo intencionadamente en la cabecera Content-Length (p. ej., declarando 1 MB y nunca enviando el último bloque) el archivo temporal permanece fijado durante ~60 segundos.
  • El código vulnerable de template de ingress-nginx permitía inyectar directivas en el config generado de nginx. Combinar eso con el archivo temporal persistente hizo posible brute-forcear los enlaces /proc/<pid>/fd/<fd> hasta que el atacante descubría el objeto compartido bufferizado.
  • Inyectar ssl_engine /proc/<pid>/fd/<fd>; forzó a nginx a cargar el .so en búfer. Los constructores dentro del objeto compartido dieron RCE inmediato dentro del pod del ingress controller, lo que a su vez expuso secretos de Kubernetes.

Un snippet de reconocimiento reducido para este estilo de ataque se ve así:

Escáner rápido de procfs ```python #!/usr/bin/env python3 import os

def find_tempfds(pid_range=range(100, 4000), fd_range=range(10, 80)): for pid in pid_range: fd_dir = f“/proc/{pid}/fd“ if not os.path.isdir(fd_dir): continue for fd in fd_range: try: path = os.readlink(f“{fd_dir}/{fd}“) if “client-body” in path or “nginx” in path: yield pid, fd, path except OSError: continue

for pid, fd, path in find_tempfds(): print(f“use ?file=/proc/{pid}/fd/{fd} # {path}“)

</details>

Ejecuta esto desde cualquier primitive (command injection, template injection, etc.) que ya tengas. Alimenta las rutas descubiertas `/proc/<pid>/fd/<fd>` de vuelta en tu parámetro LFI para incluir la payload en búfer.

## Consejos prácticos

* Cuando nginx desactiva el buffering (`proxy_request_buffering off`, `client_body_buffer_size` tuned high, or `proxy_max_temp_file_size 0`), la técnica se vuelve mucho más difícil — así que siempre enumera los archivos de configuración y las cabeceras de respuesta para comprobar si el buffering sigue habilitado.
* Hanging uploads son ruidosas pero efectivas. Usa múltiples procesos para inundar a los workers de modo que al menos un temp file permanezca el tiempo suficiente para que tu LFI brute force lo alcance.
* En Kubernetes u otros orquestadores, los límites de privilegios pueden verse diferentes, pero el primitive es el mismo: encuentra una manera de depositar bytes en los buffers de nginx y luego recorre `/proc` desde cualquier lugar donde puedas emitir lecturas del sistema de archivos.

## Labs

- [https://bierbaumer.net/security/php-lfi-with-nginx-assistance/php-lfi-with-nginx-assistance.tar.xz](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/php-lfi-with-nginx-assistance.tar.xz)
- [https://2021.ctf.link/internal/challenge/ed0208cd-f91a-4260-912f-97733e8990fd/](https://2021.ctf.link/internal/challenge/ed0208cd-f91a-4260-912f-97733e8990fd/)
- [https://2021.ctf.link/internal/challenge/a67e2921-e09a-4bfa-8e7e-11c51ac5ee32/](https://2021.ctf.link/internal/challenge/a67e2921-e09a-4bfa-8e7e-11c51ac5ee32/)

## References

- [https://bierbaumer.net/security/php-lfi-with-nginx-assistance/](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/)
- [https://www.opswat.com/blog/ingressnightmare-cve-2025-1974-remote-code-execution-vulnerability-remediation](https://www.opswat.com/blog/ingressnightmare-cve-2025-1974-remote-code-execution-vulnerability-remediation)

> [!TIP]
> Aprende y practica Hacking en AWS:<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../../../../../images/arte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">\
> Aprende y practica Hacking en GCP: <img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training GCP Red Team Expert (GRTE)**](https://training.hacktricks.xyz/courses/grte)<img src="../../../../../images/grte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
> Aprende y practica Hacking en Azure: <img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">[**HackTricks Training Azure Red Team Expert (AzRTE)**](https://training.hacktricks.xyz/courses/azrte)<img src="../../../../../images/azrte.png" alt="" style="width:auto;height:24px;vertical-align:middle;">
>
> <details>
>
> <summary>Apoya a HackTricks</summary>
>
> - Revisa los [**planes de suscripción**](https://github.com/sponsors/carlospolop)!
> - **Únete al** 💬 [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **síguenos en** **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
> - **Comparte trucos de hacking enviando PRs a los** [**HackTricks**](https://github.com/carlospolop/hacktricks) y [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repositorios de github.
>
> </details>