Nginx

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

root ロケーションが欠落している

Nginx サーバーを設定する際、root directive はファイルが配信される基準ディレクトリを定義することで重要な役割を果たします。以下の例を参照してください:

server {
root /etc/nginx;

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

この設定では、/etc/nginx が root ディレクトリに指定されています。この構成により、/hello.txt のように指定された root ディレクトリ内のファイルにアクセスできます。ただし、重要なのは特定の “location”(/hello.txt)のみが定義されている点です。root location(location / {...})の設定はありません。この省略により root ディレクティブはグローバルに適用され、ルートパス / へのリクエストが /etc/nginx 以下のファイルにアクセスできるようになります。

この設定は重大なセキュリティ上の懸念を生じさせます。GET /nginx.conf のような単純な GET リクエストにより、/etc/nginx/nginx.conf にある Nginx の設定ファイルが返され、機密情報が露出する可能性があります。root を /etc のようなより機密性の低いディレクトリに設定することでこのリスクを軽減できますが、それでも他の設定ファイル、アクセスログ、あるいは HTTP Basic 認証で使用される暗号化された資格情報など、意図しない重要なファイルにアクセスできてしまう可能性があります。

Alias を用いた LFI の誤設定

Nginx の設定ファイルでは、“location” ディレクティブの綿密な確認が必要です。Local File Inclusion (LFI) として知られる脆弱性は、次のような設定によって誤って導入されることがあります:

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

この設定は LFI attacks に対して脆弱です。サーバーが /imgs../flag.txt のようなリクエストを、意図したディレクトリの外にあるファイルへアクセスしようとする試みとして解釈し、実際には /path/images/../flag.txt に解決してしまうためです。

この脆弱性により、攻撃者はウェブ経由でアクセスできるべきではないサーバーのファイルシステム上のファイルを取得できてしまいます。

この脆弱性を軽減するため、設定は次のように調整する必要があります:

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

詳細情報: https://www.acunetix.com/vulnerabilities/web/path-traversal-via-misconfigured-nginx-alias/

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

安全でないパス制限

次のページを参照して、次のようなディレクティブを bypass する方法を学んでください:

location = /admin {
deny all;
}

location = /admin/ {
deny all;
}

Proxy / WAF Protections Bypass

危険な変数の使用 / HTTP Request Splitting

Caution

脆弱な変数 $uri$document_uri があり、これは $request_uri に置き換えることで修正できます。

正規表現(regex)も次のように脆弱になることがあります:

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

location ~ /docs/([^/\s])? { … $1 … } - 脆弱ではない(スペースをチェック)

location ~ /docs/(.*)? { … $1 … } - 脆弱ではない

Nginx 設定の脆弱性は以下の例で示されます:

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

文字 \r(キャリッジリターン)と \n(ラインフィード)は HTTP リクエストにおける改行文字を示し、それらの URL エンコードされた形は %0d%0a です。これらの文字をリクエストに含めると(例: http://localhost/%0d%0aDetectify:%20clrf)、誤設定されたサーバは Detectify という名前の新しいヘッダーを発行します。これは $uri 変数が URL エンコードされた改行文字をデコードしてしまい、レスポンスに予期しないヘッダーが含まれるためです:

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

CRLF injection and response splitting のリスクについて詳しくは https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/ を参照してください。

Also this technique is explained in this talk でいくつかの脆弱な例や検出メカニズムとともに説明されています。例えば、blackboxの視点からこのミスコンフィギュレーションを検出するには、次のリクエストを送ることができます:

  • https://example.com/%20X - 任意のHTTPコード
  • https://example.com/%20H - 400 Bad Request

脆弱であれば、最初のリクエストは “X” が任意のHTTPメソッドとして扱われ、2つ目は H が有効なメソッドではないためエラーが返されます。したがってサーバーは GET / H HTTP/1.1 のように受信し、これがエラーを引き起こします。

別の検出例は次のとおりです:

  • http://company.tld/%20HTTP/1.1%0D%0AXXXX:%20x - 任意のHTTPコード
  • http://company.tld/%20HTTP/1.1%0D%0AHost:%20x - 400 Bad Request

その講演で紹介された脆弱な設定の例のいくつかは次のとおりです:

  • 最終的なURLにおいて**$uri**がそのまま設定されている点に注意してください。
location ^~ /lite/api/ {
proxy_pass http://lite-backend$uri$is_args$args;
}
  • 再度 $uri が URL に含まれている点に注意(今回はパラメータ内)
location ~ ^/dna/payment {
rewrite ^/dna/([^/]+) /registered/main.pl?cmd=unifiedPayment&context=$1&native_uri=$uri break;
proxy_pass http://$back;
  • 今は AWS S3 にあります
location /s3/ {
proxy_pass https://company-bucket.s3.amazonaws.com$uri;
}

任意の変数

ある状況下で、ユーザー提供データがNginxの変数として扱われる可能性があることが判明しました。この挙動の原因はやや不明瞭ですが、稀というほどでもなく、検証も簡単ではありません。この異常はHackerOneのセキュリティレポートで指摘されており、hereで確認できます。エラーメッセージのさらなる調査により、その発生箇所はNginxのコードベース内のSSI filter moduleにあることが特定され、Server Side Includes (SSI)が根本原因であることが示されました。

この誤設定を検出するには、refererヘッダを設定して変数が出力されるかを確認する以下のコマンドを実行できます:

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

この誤設定をシステム全体でスキャンしたところ、ユーザーが Nginx の変数を出力できる複数の事例が明らかになりました。ただし、脆弱なインスタンスの数が減少していることから、この問題の修正作業はある程度成功していることが示唆されます。

try_files を $URI$ARGS 変数と併用する

以下の Nginx の誤設定は LFI 脆弱性につながる可能性があります:

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

設定では try_files ディレクティブがあり、指定した順序でファイルの存在を確認するために使用されます。Nginx は見つかった最初のファイルを返します。try_files ディレクティブの基本構文は次のとおりです:

try_files file1 file2 ... fileN fallback;

Nginxは指定された順序で各ファイルの存在を確認します。ファイルが存在する場合、それが直ちに提供されます。指定されたファイルのいずれも存在しない場合、リクエストは別のURIや特定のエラーページなどのフォールバックオプションに渡されます。

しかし、このディレクティブで $uri$args 変数を使用すると、NginxはリクエストURIにクエリ文字列引数を結合したものにマッチするファイルを探そうとします。したがって、この設定を悪用できます:

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

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

次のペイロードで:

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

payloadを使ってroot directory (defined in Nginx configuration) から脱出し、/etc/passwdファイルを読み込みます。debug logsでは、Nginxがどのようにファイルにアクセスしようとするかを確認できます:

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

上記の設定を使用した Nginx に対する PoC: Example burp request

バックエンドの生のレスポンスの読み取り

Nginx は proxy_pass を介して、backend から生成されるエラーや HTTP ヘッダをインターセプトして内部のエラーメッセージやヘッダを隠す機能を提供します。これは Nginx が backend エラーに対してカスタムエラーページを返すことで実現されます。しかし、Nginx が無効な HTTP リクエストに遭遇した場合には問題が生じます。そのようなリクエストは受信したまま backend に転送され、backend の生のレスポンスが Nginx の介入なしに直接 client に送信されます。

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

これを管理するために、Nginx の設定には特定のディレクティブが使用されます:

http {
error_page 500 /html/error.html;
proxy_intercept_errors on;
proxy_hide_header Secret-Header;
}
  • proxy_intercept_errors: このディレクティブは、ステータスコードが300より大きいバックエンド応答に対して Nginx がカスタムレスポンスを返すことを可能にします。たとえば我々の uWSGI アプリケーションでは、500 Error の応答が Nginx によってインターセプトされ処理されることを保証します。
  • proxy_hide_header: 名前が示す通り、このディレクティブは指定された HTTP ヘッダをクライアントから隠し、プライバシーとセキュリティを高めます。

有効な GET リクエストが行われた場合、Nginx は通常どおり処理し、秘密のヘッダを開示しない標準的なエラーレスポンスを返します。しかし、無効な HTTP リクエストはこの仕組みを回避し、生のバックエンド応答(秘密のヘッダやエラーメッセージを含む)が露出してしまいます。

merge_slashes を off に設定

デフォルトで、Nginx の merge_slashes ディレクティブon に設定されており、URL 内の連続するスラッシュを単一のスラッシュに圧縮します。この機能は URL 処理を簡素化しますが、特に local file inclusion (LFI) に脆弱なアプリケーションにおいて、Nginx の背後にある脆弱性を意図せず隠してしまう可能性があります。セキュリティ専門家の Danny Robinson and Rotem Bar は、特に Nginx がリバースプロキシとして動作する場合におけるこのデフォルト動作の潜在的リスクを指摘しています。

そのようなリスクを軽減するため、これらの脆弱性に晒されやすいアプリケーションでは merge_slashes ディレクティブを off にする ことを推奨します。これにより Nginx は URL 構造を変更せずにアプリケーションへリクエストを転送し、基礎となるセキュリティ問題を隠蔽しません。

For more information check Danny Robinson and Rotem Bar.

Maclicious Response Headers

As shown in this writeup, certain headers in the web server response can change the behaviour of the Nginx proxy. You can check them in the docs:

  • X-Accel-Redirect: Nginx に内部リダイレクトを指示し、指定した場所へリクエストを内部的に転送します。
  • X-Accel-Buffering: Nginx がレスポンスをバッファするかどうかを制御します。
  • X-Accel-Charset: X-Accel-Redirect を使用する際のレスポンスの文字セットを設定します。
  • X-Accel-Expires: X-Accel-Redirect を使用する際のレスポンスの有効期限を設定します。
  • X-Accel-Limit-Rate: X-Accel-Redirect を使用する際の転送速度を制限します。

例えば、ヘッダ X-Accel-Redirect は nginx 内部での redirect を引き起こします。したがって、nginx 設定に root / のような指定があり、Web サーバから X-Accel-Redirect: .env が返されると、nginx は /.env の内容を返送してしまいます(Path Traversal)。

Default Value in Map Directive

Nginx の configuration では、map ディレクティブがしばしば 認可制御 の役割を果たします。一般的なミスは デフォルト 値を指定しないことで、これが不正アクセスにつながる可能性があります。例えば:

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

Without a default, a 悪意のあるユーザー can bypass security by accessing an 未定義のURI within /map-poc. The Nginx manual advises setting a デフォルト値 to avoid such issues.

DNS Spoofing 脆弱性

DNS spoofing against Nginx is feasible under certain conditions. If an attacker knows the DNS server used by Nginx and can intercept its DNS queries, they can spoof DNS records. This method, however, is ineffective if Nginx is configured to use localhost (127.0.0.1) for DNS resolution. Nginx allows specifying a DNS server as follows:

resolver 8.8.8.8;

proxy_passinternal ディレクティブ

The proxy_pass ディレクティブは、リクエストを内部または外部の別のサーバーへ転送するために使用されます。The internal ディレクティブは、特定の location が Nginx 内部からのみアクセス可能であることを保証します。これらのディレクティブ自体は脆弱性ではありませんが、設定を慎重に確認しないとセキュリティ上の問題が発生する可能性があります。

proxy_set_header Upgrade & Connection

nginx サーバーが Upgrade および Connection ヘッダーを転送するように設定されている場合、h2c Smuggling attack を実行して保護された/内部のエンドポイントにアクセスできる可能性があります。

Caution

この脆弱性により攻撃者は proxy_pass エンドポイントに直接接続を確立できる(この場合 http://backend:9999)ようになり、その内容は 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

proxy_passhttp://backend:9999/socket.io のような特定の パス を指していたとしても、接続は http://backend:9999 で確立されるため、内部エンドポイント内の他の任意のパスにアクセスできる。つまり proxy_pass の URL にパスが指定されていても関係ない。

HTTP/3 QUIC module のリモート DoS & leak (2024)

2024年に Nginx は CVE-2024-31079、CVE-2024-32760、CVE-2024-34161、CVE-2024-35200 を公開し、実験的な ngx_http_v3_module が組み込まれ、listen ... quic ソケットが公開されている場合に 単一の悪意ある QUIC セッション がワーカープロセスをクラッシュさせたりメモリを leak させたりする可能性があることを示しました。影響を受けるビルドは 1.25.0–1.25.5 と 1.26.0 で、1.27.0/1.26.1 に修正が含まれます。メモリ開示(CVE-2024-34161)は、機密データが表面化するために 4096 バイトより大きい MTU を追加で必要とします(詳細は下記の 2024 nginx advisory を参照)。

Recon & exploitation hints

  • HTTP/3 はオプトイン方式なので、Alt-Svc: h3=":443" レスポンスをスキャンするか、UDP/443 で QUIC ハンドシェイクをブルートフォースしてください。確認できたら、カスタムの quiche-client/nghttp3 ペイロードでハンドシェイクや STREAM フレームを fuzz してワーカープロセスのクラッシュを誘発し、ログを強制的に leak させます。
  • ターゲットの対応を素早く判別するには:
nginx -V 2>&1 | grep -i http_v3
rg -n "listen .*quic" /etc/nginx/

TLSセッション再開によるクライアント証明書認証の回避 (CVE-2025-23419)

2025年2月のアドバイザリで、OpenSSLでビルドされた nginx 1.11.4–1.27.3 が、名前ベースのバーチャルホスト間で TLS 1.3 セッションを再利用できる 場合があることが公表されました。そのため、証明書不要のホストでネゴシエートしたクライアントがチケット/PSKをリプレイすると、ssl_verify_client on; で保護された vhost に入り込み、mTLS を完全に回避できます。バグは、複数のバーチャルホストが同じ TLS 1.3 セッションキャッシュとチケットを共有している場合に発生します(下記の2025年の nginx アドバイザリを参照)。

攻撃者プレイブック

# 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.

脆弱な場合、2回目のハンドシェイクはクライアント証明書を提示せずに完了し、保護された場所が明らかになります。

What to audit

  • Mixed server_name blocks that share ssl_session_cache shared:SSL plus ssl_session_tickets on;.
    • 公開と管理などが混在する server_name ブロックで、ssl_session_cache shared:SSLssl_session_tickets on; を共有しているもの。
  • Admin/API blocks that expect mTLS but inherit shared session cache/ticket settings from public hosts.
    • mTLS を期待する Admin/API ブロックが、公開ホストから共有の session cache/ticket 設定を継承しているもの。
  • Automation that enables TLS 1.3 session resumption globally (e.g., Ansible roles) without considering vhost isolation.
    • TLS 1.3 の session resumption をグローバルに有効化する自動化(例: Ansible roles)が vhost の分離を考慮していないもの。

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

The HTTP/2 Rapid Reset attack (CVE-2023-44487) still affects nginx when operators crank keepalive_requests or http2_max_concurrent_streams beyond the defaults: an attacker opens one HTTP/2 connection, floods it with thousands of streams, then immediately issues RST_STREAM frames so the concurrency ceiling is never reached while CPU keeps burning on tear-down logic. Nginx defaults (128 concurrent streams, 1000 keepalive requests) keep the blast radius small; pushing those limits “substantially higher” makes it trivial to starve workers even from a single client (see the F5 write-up referenced below).

HTTP/2 Rapid Reset 攻撃(CVE-2023-44487)は、オペレータが keepalive_requestshttp2_max_concurrent_streams をデフォルトより引き上げた場合に nginx に依然として影響を与えます。攻撃者は1つの HTTP/2 接続を開き、数千のストリームを流し込み、直ちに RST_STREAM フレームを送出して同時実行上限を到達させないようにしつつ、CPU を teardown ロジックで消費させ続けます。nginx のデフォルト(128 concurrent streams、1000 keepalive requests)は影響範囲を小さく保ちますが、これらの上限を「大幅に」引き上げると、単一クライアントからでもワーカーを枯渇させるのは容易になります(下記の F5 の write-up を参照)。

Detection tips 検出のヒント

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

これらのディレクティブで異常に高い値を示すホストは格好の標的です:1つの HTTP/2 クライアントがストリーム作成と即時の RST_STREAM フレームをループさせ、concurrency cap を発動させることなく CPU 使用率を高止まりさせることができます。

Try it yourself

Detectify は GitHub リポジトリを作成しました。そこでは Docker を使用して、本記事で取り上げた誤設定のいくつかを含む脆弱な Nginx テストサーバを立て、実際に見つけてみることができます!

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

Static Analyzer tools

gixy-ng & Gixy-Next & GIXY

  • Gixy-Next (an updated fork of GIXY) は Nginx 設定を解析するツールで、脆弱性、不安全なディレクティブ、リスクのある誤設定を見つけることを目的としています。パフォーマンスに影響する誤設定や、見逃されたハードニングの機会も検出し、自動化された欠陥検出を可能にします。
  • gixy-ng (the actively maintained fork of GIXY) は Nginx 設定を解析するツールで、脆弱性、不安全なディレクティブ、リスクのある誤設定を見つけることを目的としています。パフォーマンスに影響する誤設定や、見逃されたハードニングの機会も検出し、自動化された欠陥検出を可能にします。

Nginxpwner

Nginxpwner は一般的な Nginx の誤設定や脆弱性を探すためのシンプルなツールです。

References

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする