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 directiva root 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 crucial señalar 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 las solicitudes a la ruta raíz / accedan a archivos bajo /etc/nginx.

Surge una consideración crítica de seguridad a partir de esta configuración. Una simple solicitud 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; no obstante, aún podría permitir el acceso no deseado a otros archivos críticos, incluidos otros archivos de configuración, registros de acceso e incluso credenciales cifradas usadas para la autenticación HTTP básica.

Misconfiguración de Alias LFI

En los archivos de configuración de Nginx, conviene inspeccionar detenidamente las directivas “location”. Una vulnerabilidad conocida como Local File Inclusion (LFI) puede introducirse inadvertidamente a través de 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, resolviéndolas efectivamente a /path/images/../flag.txt. Esta vulnerabilidad permite a los atacantes recuperar archivos del sistema de archivos del servidor que no deberían ser accesibles vía web.

Para mitigar esta vulnerabilidad, la configuración debe 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 de ruta insegura

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

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

Uso inseguro de variables / HTTP Request Splitting

Caution

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

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

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

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

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

Una vulnerabilidad en la configuración de Nginx se demuestra con el siguiente ejemplo:

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

Los caracteres \r (Retorno de carro) y \n (Salto de línea) indican caracteres de nueva línea en solicitudes HTTP, y sus formas codificadas en URL se representan como %0d%0a. Incluir estos caracteres en una solicitud (p. ej., http://localhost/%0d%0aDetectify:%20clrf) a un servidor mal configurado hace que el servidor emita un nuevo encabezado llamado Detectify. Esto ocurre porque la variable $uri decodifica los caracteres de nueva línea codificados en URL, lo que conduce a un encabezado 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

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

Además, esta técnica está explicada en esta charla con algunos ejemplos vulnerables y mecanismos de detección. Por ejemplo, para detectar esta mala configuración desde una perspectiva blackbox podrías enviar estas peticiones:

  • https://example.com/%20X - Cualquier código HTTP
  • https://example.com/%20H - 400 Bad Request

Si es vulnerable, la primera devolverá respuesta ya que “X” es cualquier HTTP method 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 esto provocará el error.

Otros ejemplos de detección serían:

  • http://company.tld/%20HTTP/1.1%0D%0AXXXX:%20x - Cualquier código HTTP
  • http://company.tld/%20HTTP/1.1%0D%0AHost:%20x - 400 Bad Request

Algunas configuraciones vulnerables encontradas presentadas en esa charla fueron:

  • Nótese cómo $uri se establece tal cual en la URL final
location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}
  • Observa 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 user-supplied data podría ser tratada como una Nginx variable bajo ciertas circunstancias. La causa de este comportamiento sigue siendo algo esquiva, aunque no es rara ni sencilla 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 identificar su aparición dentro del SSI filter module of Nginx’s codebase, señalando a Server Side Includes (SSI) como la causa raíz.

Para detectar esta misconfiguración, se puede ejecutar el siguiente comando, que implica establecer un referer header para probar la impresión de variables:

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

Scans for this misconfiguration across systems revealed multiple instances where Nginx variables could be printed by a user. However, a decrease in the number of vulnerable instances suggests that efforts to patch this issue have been somewhat successful.

Uso de try_files con variables $URI$ARGS

La siguiente misconfiguració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 solicitud se enviará a la opción de fallback, que puede ser otro URI o una página de error específica.

Sin embargo, al usar las variables $uri$args en esta directiva, Nginx intentará buscar un archivo que coincida con el URI de la solicitud combinado 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 el siguiente payload:

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

Usando nuestro payload escaparemos del directorio root (definido en la configuración de Nginx) y cargaremos el archivo /etc/passwd. En los logs de depuración podemos observar cómo Nginx prueba 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 recibe 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.

Considere un ejemplo con 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 las respuestas del backend con un código de estado mayor que 300. Garantiza que, para nuestra aplicación uWSGI de ejemplo, una respuesta 500 Error sea interceptada y gestionada por Nginx.
  • proxy_hide_header: Como su nombre indica, esta directiva oculta cabeceras HTTP especificadas al cliente, mejorando la privacidad y la seguridad.

Cuando se realiza una solicitud válida GET, Nginx la procesa normalmente, devolviendo una respuesta de error estándar sin revelar cabeceras secretas. Sin embargo, una solicitud HTTP inválida elude este mecanismo, provocando la exposición de respuestas crudas del backend, incluidas cabeceras secretas 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 inclinadas en una URL a una sola. Esta característica, aunque agiliza el procesamiento de URLs, puede ocultar inadvertidamente vulnerabilidades en aplicaciones detrás de Nginx, particularmente aquellas susceptibles a ataques de local file inclusion (LFI). Los expertos en seguridad Danny Robinson y Rotem Bar han destacado 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 así enmascarar problemas de seguridad subyacentes.

For more information check Danny Robinson and Rotem Bar.

Maclicious Response Headers

As shown in this writeup, there are certain headers that if present in the response from the web server they will change the behaviour of the Nginx proxy. You can check them 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 o no hacer buffering de la respuesta.
  • X-Accel-Charset: Establece el conjunto 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 de las respuestas al usar X-Accel-Redirect.

Por ejemplo, la cabecera X-Accel-Redirect provocará una redirección interna en nginx. Por lo tanto, 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 desempeña un papel en el control de autorizaciones. Un error común es no especificar un valor default, lo que podría conducir 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 un URI no definido dentro de /map-poc. The Nginx manual aconseja establecer un default value para evitar dichos problemas.

DNS Spoofing Vulnerability

DNS spoofing contra Nginx es factible bajo ciertas condiciones. Si un atacante conoce el DNS server usado por 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 DNS server de la siguiente manera:

resolver 8.8.8.8;

proxy_pass y internal Directivas

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

proxy_set_header Upgrade & Connection

Si el servidor nginx está configurado para pasar los encabezados 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á verificado por nginx.

Example of vulnerable configuration to steal /flag from 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

Tenga 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 puede contactar cualquier otra ruta dentro de ese endpoint interno. Así que no importa si se especifica una ruta en la URL de proxy_pass.

Módulo HTTP/3 QUIC remote DoS & leak (2024)

Durante 2024 Nginx divulgó CVE-2024-31079, CVE-2024-32760, CVE-2024-34161 y CVE-2024-35200 mostrando que una sola sesión QUIC hostil puede hacer caer procesos worker o leak de memoria siempre que el experimental ngx_http_v3_module esté compilado y se exponga un socket listen ... quic. Las versiones afectadas son 1.25.0–1.25.5 y 1.26.0, mientras que 1.27.0/1.26.1 incluyen las correcciones; la divulgación de memoria (CVE-2024-34161) adicionalmente requiere MTUs mayores de 4096 bytes para que datos sensibles afloren (detalles en el advisory de nginx de 2024 referenciado más abajo).

Pistas de reconocimiento y explotación

  • HTTP/3 es opt-in, así que escanea en busca de respuestas Alt-Svc: h3=":443" o fuerza handshakes QUIC en UDP/443; una vez confirmado, fuzzea el handshake y los frames STREAM con payloads personalizados de quiche-client/nghttp3 para provocar crashes en los worker processes y forzar leak en los logs.
  • Identifica rápidamente el soporte del objetivo con:
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/

Bypass de reanudación de sesión TLS de la autenticación con certificado de cliente (CVE-2025-23419)

Un advisory de febrero de 2025 divulgó 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 omitir mTLS por completo. El bug se desencadena siempre que múltiples virtual hosts comparten la misma caché de sesión y tickets de TLS 1.3 (ver el advisory de nginx de 2025 referenciado más abajo).

Plan de ataque

# 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 certificado de cliente, revelando ubicaciones protegidas.

Qué auditar

  • Bloques server_name mezclados que comparten ssl_session_cache shared:SSL además de ssl_session_tickets on;.
  • Bloques Admin/API que esperan mTLS pero heredan la configuración compartida de caché de sesión/tickets desde hosts públicos.
  • Automatización que habilita la reanudación de sesión de TLS 1.3 globalmente (p. ej., roles de Ansible) sin considerar el aislamiento de vhosts.

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

El ataque HTTP/2 Rapid Reset (CVE-2023-44487) todavía afecta a nginx cuando los operadores suben 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 enseguida envía frames 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 defaults de nginx (128 concurrent streams, 1000 keepalive requests) mantienen pequeño el radio de impacto; aumentar esos límites “sustancialmente” hace trivial dejar a los workers sin recursos incluso desde un único 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 revelan valores inusualmente altos para esas directivas son objetivos principales: un cliente HTTP/2 puede iterar creando streams y enviando instantáneamente tramas 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 configurar tu propio servidor de prueba vulnerable Nginx con algunas de las misconfiguraciones discutidas en este artículo y tratar de encontrarlas por ti mismo!

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

Herramientas de análisis estático

GixyNG & GIXY

GixyNG (un fork actualizado de GIXY) es una herramienta para analizar configuraciones de Nginx, con el objetivo de encontrar vulnerabilidades, directivas inseguras y misconfiguraciones arriesgadas. También detecta misconfiguraciones que afectan al rendimiento y detecta oportunidades de hardening no aplicadas, permitiendo la detección automatizada de fallos.

Nginxpwner

Nginxpwner es una herramienta sencilla para buscar misconfiguraciones 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