Nginx

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

Ubicación root faltante

Al configurar el servidor Nginx, la root directive desempeña un papel crítico al definir el directorio base desde el cual se sirven los archivos. Considere el ejemplo a continuación:

server {
root /etc/nginx;

location /hello.txt {
try_files $uri $uri/ =404;
proxy_pass http://127.0.0.1:8080/;
}
}

En esta configuración, /etc/nginx está designado como el directorio root. Esta configuración permite el acceso a archivos dentro del directorio root especificado, como /hello.txt. Sin embargo, es importante notar que solo se define una ubicación específica (/hello.txt). No existe una configuración para la ubicación raíz (location / {...}). Esta omisión significa que la directiva root se aplica de forma global, permitiendo que solicitudes a la ruta raíz / accedan a archivos bajo /etc/nginx.

Surge una consideración crítica de seguridad con esta configuración. Una simple petición GET, como GET /nginx.conf, podría exponer información sensible al servir el archivo de configuración de Nginx ubicado en /etc/nginx/nginx.conf. Establecer el root a un directorio menos sensible, como /etc, podría mitigar este riesgo; sin embargo, aún podría permitir el acceso no intencionado a otros archivos críticos, incluidos otros archivos de configuración, registros de acceso y hasta credenciales cifradas usadas para HTTP basic authentication.

Mala configuración de Alias LFI

En los archivos de configuración de Nginx, conviene inspeccionar cuidadosamente las directivas “location”. Una vulnerabilidad conocida como Local File Inclusion (LFI) puede introducirse de forma inadvertida mediante una configuración que se asemeje a la siguiente:

location /imgs {
alias /path/images/;
}

Esta configuración es vulnerable a ataques LFI debido a que el servidor interpreta solicitudes como /imgs../flag.txt como un intento de acceder a archivos fuera del directorio previsto, resolviendo efectivamente a /path/images/../flag.txt. Esta falla permite a los atacantes recuperar archivos del sistema de archivos del servidor que no deberían ser accesibles a través de la web.

Para mitigar esta vulnerabilidad, la configuración debería ajustarse para:

location /imgs/ {
alias /path/images/;
}

Más información: https://www.acunetix.com/vulnerabilities/web/path-traversal-via-misconfigured-nginx-alias/

Pruebas de Accunetix:

alias../ => HTTP status code 403
alias.../ => HTTP status code 404
alias../../ => HTTP status code 403
alias../../../../../../../../../../../ => HTTP status code 400
alias../ => HTTP status code 403

Restricción insegura de rutas

Consulta la siguiente página para aprender cómo bypass directivas como:

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

Uso inseguro de variables / HTTP Request Splitting

Caution

Variables vulnerables $uri y $document_uri; esto puede solucionarse reemplazándolas por $request_uri.

Un regex también puede ser vulnerable, por ejemplo:

location ~ /docs/([^/])? { … $1 … } - Vulnerable

location ~ /docs/([^/\s])? { … $1 … } - No vulnerable (comprobando espacios)

location ~ /docs/(.*)? { … $1 … } - No vulnerable

Se demuestra una vulnerabilidad en la configuración de Nginx con el siguiente ejemplo:

location / {
return 302 https://example.com$uri;
}

Los caracteres \r (Carriage Return) y \n (Line Feed) indican caracteres de nueva línea en las peticiones HTTP, y sus formas codificadas en URL se representan como %0d%0a. Incluir estos caracteres en una solicitud (por ejemplo, http://localhost/%0d%0aDetectify:%20clrf) a un servidor mal configurado provoca que el servidor emita un nuevo header llamado Detectify. Esto ocurre porque la variable $uri decodifica los caracteres de nueva línea codificados en URL, dando lugar a un header inesperado en la respuesta:

HTTP/1.1 302 Moved Temporarily
Server: nginx/1.19.3
Content-Type: text/html
Content-Length: 145
Connection: keep-alive
Location: https://example.com/
Detectify: clrf

Aprende más sobre los riesgos de CRLF injection y response splitting en https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/.

Also this technique is explained in this talk with some vulnerable examples and dectection mechanisms. For example, In order to detect this misconfiguration from a blackbox perspective you could these requests:

  • https://example.com/%20X - Any HTTP code
  • https://example.com/%20H - 400 Bad Request

Si es vulnerable, la primera responderá con un código cualquiera porque X puede ser cualquier método HTTP, y la segunda devolverá un error porque H no es un método válido. Así, el servidor recibirá algo como: GET / H HTTP/1.1 y eso provocará el error.

Otros ejemplos de detección serían:

  • http://company.tld/%20HTTP/1.1%0D%0AXXXX:%20x - Any HTTP code
  • http://company.tld/%20HTTP/1.1%0D%0AHost:%20x - 400 Bad Request

Algunas configuraciones vulnerables presentadas en esa charla fueron:

  • Nótese cómo $uri está establecido tal cual en la URL final
location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}
  • Fíjate cómo, de nuevo, $uri está en la URL (esta vez dentro de un parámetro)
location ~ ^/dna/payment {
rewrite ^/dna/([^/]+) /registered/main.pl?cmd=unifiedPayment&context=$1&native_uri=$uri break;
proxy_pass http://$back;
  • Ahora en AWS S3
location /s3/ {
proxy_pass https://company-bucket.s3.amazonaws.com$uri;
}

Cualquier variable

Se descubrió que los datos proporcionados por el usuario podrían ser tratados como una variable de Nginx en determinadas circunstancias. La causa de este comportamiento sigue siendo algo enigmática, aunque no es infrecuente ni fácil de verificar. Esta anomalía fue destacada en un informe de seguridad en HackerOne, que puede verse here. Una investigación adicional del mensaje de error llevó a la identificación de su aparición dentro del SSI filter module of Nginx’s codebase, señalando Server Side Includes (SSI) como la causa raíz.

Para detectar esta misconfiguración, se puede ejecutar el siguiente comando, el cual implica establecer la cabecera Referer para comprobar la impresión de variables:

$ curl -H ‘Referer: bar’ http://localhost/foo$http_referer | grep ‘foobar’

Los escaneos en busca de esta mala configuración en sistemas revelaron múltiples instancias en las que un usuario podía imprimir variables de Nginx. Sin embargo, la disminución en el número de instancias vulnerables sugiere que los esfuerzos para parchear este problema han tenido cierto éxito.

Usando try_files con variables $URI$ARGS

La siguiente mala configuración de Nginx puede conducir a una vulnerabilidad LFI:

location / {
try_files $uri$args $uri$args/ /index.html;
}

En nuestra configuración tenemos la directiva try_files, que se usa para comprobar la existencia de archivos en un orden especificado. Nginx servirá el primero que encuentre. La sintaxis básica de la directiva try_files es la siguiente:

try_files file1 file2 ... fileN fallback;

Nginx comprobará la existencia de cada archivo en el orden especificado. Si un archivo existe, se servirá inmediatamente. Si ninguno de los archivos especificados existe, la petición se pasará a la opción de fallback, que puede ser otro URI o una página de error específica.

Sin embargo, cuando se usan las variables $uri$args en esta directiva, Nginx intentará buscar un archivo que coincida con la URI de la petición combinada con cualquier argumento de la cadena de consulta. Por lo tanto podemos explotar esta configuración:

http {
server {
root /var/www/html/public;

location / {
try_files $uri$args $uri$args/ /index.html;
}
}
}

Con la siguiente carga útil:

GET /?../../../../../../../../etc/passwd HTTP/1.1
Host: example.com

Usando nuestro payload escaparemos del directorio raíz (definido en la configuración de Nginx) y cargaremos el archivo /etc/passwd. En los registros de depuración podemos observar cómo Nginx intenta los archivos:

...SNIP...

2025/07/11 15:49:16 [debug] 79694#79694: *4 trying to use file: "/../../../../../../../../etc/passwd" "/var/www/html/public/../../../../../../../../etc/passwd"
2025/07/11 15:49:16 [debug] 79694#79694: *4 try file uri: "/../../../../../../../../etc/passwd"

...SNIP...

2025/07/11 15:49:16 [debug] 79694#79694: *4 http filename: "/var/www/html/public/../../../../../../../../etc/passwd"

...SNIP...

2025/07/11 15:49:16 [debug] 79694#79694: *4 HTTP/1.1 200 OK

PoC contra Nginx usando la configuración mencionada arriba: Example burp request

Lectura de la respuesta cruda del backend

Nginx ofrece una funcionalidad mediante proxy_pass que permite interceptar errores y cabeceras HTTP generadas por el backend, con el objetivo de ocultar mensajes de error internos y cabeceras. Esto se logra haciendo que Nginx sirva páginas de error personalizadas en respuesta a errores del backend. Sin embargo, surgen problemas cuando Nginx encuentra una petición HTTP inválida. Dicha petición se reenvía al backend tal como fue recibida, y la respuesta cruda del backend se envía directamente al cliente sin la intervención de Nginx.

Considera un escenario de ejemplo que involucra una aplicación uWSGI:

def application(environ, start_response):
start_response('500 Error', [('Content-Type', 'text/html'), ('Secret-Header', 'secret-info')])
return [b"Secret info, should not be visible!"]

Para gestionar esto, se usan directivas específicas en la configuración de Nginx:

http {
error_page 500 /html/error.html;
proxy_intercept_errors on;
proxy_hide_header Secret-Header;
}
  • proxy_intercept_errors: Esta directiva permite a Nginx servir una respuesta personalizada para respuestas del backend con un código de estado mayor a 300. Asegura que, para nuestra aplicación uWSGI de ejemplo, un 500 Error sea interceptado y manejado por Nginx.
  • proxy_hide_header: Como su nombre indica, esta directiva oculta encabezados HTTP especificados del cliente, mejorando la privacidad y la seguridad.

Cuando se realiza una solicitud GET válida, Nginx la procesa normalmente, devolviendo una respuesta de error estándar sin revelar ningún encabezado secreto. Sin embargo, una petición HTTP inválida elude este mecanismo, lo que resulta en la exposición de respuestas crudas del backend, incluidos encabezados secretos y mensajes de error.

merge_slashes set to off

Por defecto, la directiva merge_slashes de Nginx está configurada en on, lo que comprime múltiples barras diagonales en una URL en una sola barra. Esta función, aunque agiliza el procesamiento de URL, puede inadvertidamente ocultar vulnerabilidades en las aplicaciones detrás de Nginx, especialmente aquellas propensas a local file inclusion (LFI). Los expertos en seguridad Danny Robinson y Rotem Bar han señalado los riesgos potenciales asociados con este comportamiento por defecto, especialmente cuando Nginx actúa como reverse-proxy.

Para mitigar dichos riesgos, se recomienda desactivar la directiva merge_slashes para aplicaciones susceptibles a estas vulnerabilidades. Esto garantiza que Nginx reenvíe las solicitudes a la aplicación sin alterar la estructura de la URL, evitando enmascarar problemas de seguridad subyacentes.

For more information check Danny Robinson and Rotem Bar.

Maclicious Response Headers

Como se muestra en this writeup, existen ciertos encabezados que, si están presentes en la respuesta del servidor web, cambiarán el comportamiento del proxy de Nginx. Puedes consultarlos in the docs:

  • X-Accel-Redirect: Indica a Nginx que redirija internamente una solicitud a una ubicación especificada.
  • X-Accel-Buffering: Controla si Nginx debe bufferizar la respuesta o no.
  • X-Accel-Charset: Establece el juego de caracteres para la respuesta al usar X-Accel-Redirect.
  • X-Accel-Expires: Establece el tiempo de expiración para la respuesta al usar X-Accel-Redirect.
  • X-Accel-Limit-Rate: Limita la tasa de transferencia para respuestas al usar X-Accel-Redirect.

Por ejemplo, el encabezado X-Accel-Redirect provocará una redirect interna en el nginx. Así, tener una configuración de nginx con algo como root / y una respuesta del servidor web con X-Accel-Redirect: .env hará que nginx envíe el contenido de /.env (Path Traversal).

Default Value in Map Directive

En la configuración de Nginx, la directiva map a menudo juega un papel en el control de autorización. Un error común es no especificar un valor default, lo que podría llevar a acceso no autorizado. Por ejemplo:

http {
map $uri $mappocallow {
/map-poc/private 0;
/map-poc/secret 0;
/map-poc/public 1;
}
}
server {
location /map-poc {
if ($mappocallow = 0) {return 403;}
return 200 "Hello. It is private area: $mappocallow";
}
}

Sin un default, un usuario malicioso puede eludir la seguridad accediendo a una URI no definida dentro de /map-poc. The Nginx manual aconseja establecer un valor por defecto para evitar dichos problemas.

DNS Spoofing Vulnerabilidad

DNS spoofing contra Nginx es factible bajo ciertas condiciones. Si un atacante conoce el servidor DNS que usa Nginx y puede interceptar sus consultas DNS, puede falsificar registros DNS. Sin embargo, este método es ineficaz si Nginx está configurado para usar localhost (127.0.0.1) para la resolución DNS. Nginx permite especificar un servidor DNS de la siguiente manera:

resolver 8.8.8.8;

Directivas proxy_pass y internal

La directiva proxy_pass se utiliza para redirigir peticiones a otros servidores, ya sea de forma interna o externa. La directiva internal asegura que ciertas ubicaciones solo sean accesibles desde Nginx. Aunque estas directivas no son vulnerabilidades por sí mismas, su configuración requiere un examen cuidadoso para prevenir fallos de seguridad.

proxy_set_header Upgrade & Connection

Si el servidor nginx está configurado para pasar las cabeceras Upgrade y Connection, se podría realizar un h2c Smuggling attack para acceder a endpoints protegidos/internos.

Caution

Esta vulnerabilidad permitiría a un atacante establecer una conexión directa con el proxy_pass endpoint (http://backend:9999 en este caso) cuyo contenido no será comprobado por nginx.

Ejemplo de configuración vulnerable para robar /flag desde here:

server {
listen       443 ssl;
server_name  localhost;

ssl_certificate       /usr/local/nginx/conf/cert.pem;
ssl_certificate_key   /usr/local/nginx/conf/privkey.pem;

location / {
proxy_pass http://backend:9999;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}

location /flag {
deny all;
}

Warning

Ten en cuenta que incluso si proxy_pass apuntaba a una ruta específica como http://backend:9999/socket.io la conexión se establecerá con http://backend:9999 por lo que puedes contactar cualquier otra ruta dentro de ese endpoint interno. Así que no importa si una ruta está especificada en la URL de proxy_pass.

HTTP/3 QUIC module remote DoS & leak (2024)

Durante 2024 nginx divulgó CVE-2024-31079, CVE-2024-32760, CVE-2024-34161 y CVE-2024-35200 mostrando que una única sesión QUIC hostil puede crashear procesos worker o leak memoria siempre que el experimental ngx_http_v3_module esté compilado y se exponga un socket listen ... quic. Las builds afectadas son 1.25.0–1.25.5 y 1.26.0, mientras que 1.27.0/1.26.1 incluyen los fixes; la divulgación de memoria (CVE-2024-34161) además requiere MTUs mayores a 4096 bytes para exponer datos sensibles (detalles en el nginx advisory de 2024 referenciado abajo).

Recon & exploitation hints

  • HTTP/3 es optativo, así que escanea respuestas Alt-Svc: h3=":443" o realiza brute-force de QUIC handshakes en UDP/443; una vez confirmado, fuzz the handshake and STREAM frames con payloads personalizados de quiche-client/nghttp3 para provocar worker crashes y forzar log leakage.
  • Comprueba rápidamente si el objetivo lo soporta con:
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/

Evasión de reanudación de sesión TLS de autenticación por certificado de cliente (CVE-2025-23419)

Un advisory de febrero de 2025 reveló que nginx 1.11.4–1.27.3 compilado con OpenSSL permite reutilizar una sesión TLS 1.3 de un virtual host basado en nombre dentro de otro, de modo que un cliente que negoció un host sin certificado puede reproducir el ticket/PSK para saltar a un vhost protegido con ssl_verify_client on; y evitar mTLS por completo. El bug se activa siempre que múltiples virtual hosts comparten la misma caché de sesión y tickets de TLS 1.3 (véase el advisory de nginx de 2025 referenciado abajo).

Playbook del atacante

# 1. Create a TLS session on the public vhost and save the session ticket
openssl s_client -connect public.example.com:443 -sess_out ticket.pem

# 2. Replay that session ticket against the mTLS vhost before it expires
openssl s_client -connect admin.example.com:443 -sess_in ticket.pem -ign_eof

Si el objetivo es vulnerable, el segundo handshake se completa sin presentar un client certificate, revelando ubicaciones protegidas.

Qué auditar

  • Bloques server_name mixtos que comparten ssl_session_cache shared:SSL más ssl_session_tickets on;.
  • Bloques Admin/API que esperan mTLS pero heredan shared session cache/ticket settings desde hosts públicos.
  • Automatización que habilita TLS 1.3 session resumption globalmente (p. ej., Ansible roles) sin considerar vhost isolation.

Resiliencia frente a HTTP/2 Rapid Reset (comportamiento CVE-2023-44487)

El ataque HTTP/2 Rapid Reset (CVE-2023-44487) sigue afectando a nginx cuando los operadores incrementan keepalive_requests o http2_max_concurrent_streams por encima de los valores por defecto: un atacante abre una conexión HTTP/2, la inunda con miles de streams y luego emite inmediatamente tramas RST_STREAM para que el techo de concurrencia nunca se alcance mientras la CPU sigue consumiéndose en la lógica de tear-down. Los valores por defecto de nginx (128 concurrent streams, 1000 keepalive requests) mantienen pequeño el radio de impacto; aumentar esos límites “sustancialmente” facilita agotar los workers incluso desde un solo cliente (ver el write-up de F5 referenciado abajo).

Consejos de detección

# Highlight risky knobs
rg -n "http2_max_concurrent_streams" /etc/nginx/
rg -n "keepalive_requests" /etc/nginx/

Los hosts que muestran valores inusualmente altos para esas directivas son objetivos principales: un cliente HTTP/2 puede iterar la creación de streams y enviar cuadros instantáneos RST_STREAM para mantener la CPU al máximo sin activar el límite de concurrencia.

Pruébalo tú mismo

Detectify ha creado un repositorio en GitHub donde puedes usar Docker para montar tu propio servidor de prueba Nginx vulnerable con algunas de las malas configuraciones discutidas en este artículo y tratar de encontrarlas tú mismo!

https://github.com/detectify/vulnerable-nginx

Herramientas de análisis estático

gixy-ng & Gixy-Next & GIXY

  • Gixy-Next (un fork actualizado de GIXY) es una herramienta para analizar configuraciones de Nginx, con el objetivo de encontrar vulnerabilidades, directivas inseguras y malas configuraciones riesgosas. También detecta malas configuraciones que afectan al rendimiento y detecta oportunidades de hardening no aplicadas, permitiendo la detección automatizada de fallos.
  • gixy-ng (el fork mantenido activamente de GIXY) es una herramienta para analizar configuraciones de Nginx, con el objetivo de encontrar vulnerabilidades, directivas inseguras y malas configuraciones riesgosas. También detecta malas configuraciones que afectan al rendimiento y detecta oportunidades de hardening no aplicadas, permitiendo la detección automatizada de fallos.

Nginxpwner

Nginxpwner es una herramienta simple para buscar malas configuraciones y vulnerabilidades comunes de Nginx.

Referencias

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