# Nginx {{#include ../../banners/hacktricks-training.md}} ## Missing root location Nginxサーバーを構成する際、**rootディレクティブ**はファイルが提供される基本ディレクトリを定義する重要な役割を果たします。以下の例を考えてみてください: ```bash server { root /etc/nginx; location /hello.txt { try_files $uri $uri/ =404; proxy_pass http://127.0.0.1:8080/; } } ``` この構成では、`/etc/nginx` がルートディレクトリとして指定されています。この設定により、指定されたルートディレクトリ内のファイル、例えば `/hello.txt` へのアクセスが可能になります。しかし、特定の場所(`/hello.txt`)のみが定義されていることに注意することが重要です。ルートロケーション(`location / {...}`)の設定はありません。この省略により、ルートディレクティブはグローバルに適用され、ルートパス `/` へのリクエストが `/etc/nginx` の下のファイルにアクセスできるようになります。 この構成から生じる重要なセキュリティ上の考慮事項があります。単純な `GET` リクエスト、例えば `GET /nginx.conf` は、`/etc/nginx/nginx.conf` にある Nginx 設定ファイルを提供することで、機密情報を露出させる可能性があります。ルートを `/etc` のようなあまり機密性の高くないディレクトリに設定することで、このリスクを軽減できますが、それでも他の重要なファイル、他の設定ファイル、アクセスログ、さらには HTTP ベーシック認証に使用される暗号化された資格情報への意図しないアクセスを許可する可能性があります。 ## Alias LFI Misconfiguration Nginx の設定ファイルでは、「location」ディレクティブを注意深く検査する必要があります。Local File Inclusion (LFI) として知られる脆弱性は、以下のような構成を通じて意図せず導入される可能性があります。 ``` location /imgs { alias /path/images/; } ``` この設定は、サーバーが `/imgs../flag.txt` のようなリクエストを意図したディレクトリの外にあるファイルにアクセスしようとする試みとして解釈するため、LFI攻撃に対して脆弱です。これは実際には `/path/images/../flag.txt` に解決されます。この欠陥により、攻撃者はウェブ経由でアクセスできるべきではないサーバーのファイルシステムからファイルを取得することができます。 この脆弱性を軽減するために、設定を次のように調整する必要があります: ``` location /imgs/ { alias /path/images/; } ``` More info: [https://www.acunetix.com/vulnerabilities/web/path-traversal-via-misconfigured-nginx-alias/](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 ``` ## Unsafe path restriction 次のページを確認して、次のようなディレクティブをバイパスする方法を学んでください: ```plaintext location = /admin { deny all; } location = /admin/ { deny all; } ``` {{#ref}} ../../pentesting-web/proxy-waf-protections-bypass.md {{#endref}} ## Unsafe variable use / HTTP Request Splitting > [!CAUTION] > 脆弱な変数 `$uri` と `$document_uri` があり、これを `$request_uri` に置き換えることで修正できます。 > > 正規表現も脆弱である可能性があります: > > `location ~ /docs/([^/])? { … $1 … }` - 脆弱 > > `location ~ /docs/([^/\s])? { … $1 … }` - 脆弱でない(スペースをチェック) > > `location ~ /docs/(.*)? { … $1 … }` - 脆弱でない Nginx 設定の脆弱性は、以下の例で示されています: ``` location / { return 302 https://example.com$uri; } ``` HTTPリクエストにおいて、文字 \r (キャリッジリターン) と \n (ラインフィード) は新しい行の文字を示し、その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インジェクションとレスポンススプリッティングのリスクについて詳しく学ぶには、[https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/](https://blog.detectify.com/2019/06/14/http-response-splitting-exploitations-and-mitigations/)を参照してください。 この技術は[**このトークで説明されています**](https://www.youtube.com/watch?v=gWQyWdZbdoY&list=PL0xCSYnG_iTtJe2V6PQqamBF73n7-f1Nr&index=77)が、いくつかの脆弱な例と検出メカニズムが含まれています。例えば、ブラックボックスの観点からこの誤設定を検出するには、次のリクエストを使用できます: - `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 そのトークで示された脆弱な構成のいくつかは次のとおりです: - **`$uri`** が最終URLにそのまま設定されていることに注意してください。 ``` 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; } ``` ### Any variable **ユーザー提供データ**が特定の状況下で**Nginx変数**として扱われる可能性があることが発見されました。この挙動の原因はやや不明ですが、珍しいことではなく、確認するのも簡単ではありません。この異常はHackerOneのセキュリティレポートで強調されており、[こちら](https://hackerone.com/reports/370094)で見ることができます。エラーメッセージのさらなる調査により、[NginxのコードベースのSSIフィルターモジュール](https://github.com/nginx/nginx/blob/2187586207e1465d289ae64cedc829719a048a39/src/http/modules/ngx_http_ssi_filter_module.c#L365)内での発生が特定され、サーバーサイドインクルード(SSI)が根本的な原因であることが明らかになりました。 この**誤設定を検出する**ために、変数の印刷をテストするためにリファラーヘッダーを設定するコマンドを実行できます: ```bash $ curl -H ‘Referer: bar’ http://localhost/foo$http_referer | grep ‘foobar’ ``` この設定ミスに関するスキャンは、ユーザーによってNginx変数が印刷される複数のインスタンスを明らかにしました。しかし、脆弱なインスタンスの数の減少は、この問題を修正するための努力がある程度成功していることを示唆しています。 ## 生のバックエンドレスポンスの読み取り Nginxは、バックエンドによって生成されたエラーやHTTPヘッダーを傍受するための機能を`proxy_pass`を通じて提供し、内部エラーメッセージやヘッダーを隠すことを目的としています。これは、Nginxがバックエンドエラーに応じてカスタムエラーページを提供することによって実現されます。しかし、Nginxが無効なHTTPリクエストに遭遇すると、問題が発生します。そのようなリクエストは、受信した通りにバックエンドに転送され、バックエンドの生のレスポンスはNginxの介入なしにクライアントに直接送信されます。 uWSGIアプリケーションに関する例を考えてみましょう: ```python 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**](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors): このディレクティブは、Nginxがステータスコードが300を超えるバックエンドレスポンスに対してカスタムレスポンスを提供できるようにします。これにより、例として挙げたuWSGIアプリケーションにおいて、`500 Error`レスポンスがNginxによって捕捉され、処理されることが保証されます。 - [**proxy_hide_header**](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_hide_header): 名前が示すように、このディレクティブは指定されたHTTPヘッダーをクライアントから隠し、プライバシーとセキュリティを向上させます。 有効な`GET`リクエストが行われると、Nginxは通常通り処理し、秘密のヘッダーを明らかにすることなく標準のエラーレスポンスを返します。しかし、無効なHTTPリクエストはこのメカニズムをバイパスし、生のバックエンドレスポンスが露出し、秘密のヘッダーやエラーメッセージが含まれる結果となります。 ## merge_slashesをオフに設定 デフォルトでは、Nginxの**`merge_slashes`ディレクティブ**は**`on`**に設定されており、URL内の複数のスラッシュを1つのスラッシュに圧縮します。この機能はURL処理を簡素化しますが、特にローカルファイルインクルージョン(LFI)攻撃に対して脆弱なアプリケーションの背後にある脆弱性を隠す可能性があります。セキュリティ専門家の**Danny RobinsonとRotem Bar**は、このデフォルトの動作に関連する潜在的なリスクを指摘しています。特にNginxがリバースプロキシとして機能する場合です。 このようなリスクを軽減するために、これらの脆弱性に対して感受性のあるアプリケーションには**`merge_slashes`ディレクティブをオフにする**ことが推奨されます。これにより、NginxはURL構造を変更することなくアプリケーションにリクエストを転送し、基盤となるセキュリティ問題を隠さないようにします。 詳細については、[Danny RobinsonとRotem Bar](https://medium.com/appsflyer/nginx-may-be-protecting-your-applications-from-traversal-attacks-without-you-even-knowing-b08f882fd43d)を確認してください。 ### **Macliciousレスポンスヘッダー** [**この書き込み**](https://mizu.re/post/cors-playground)に示されているように、ウェブサーバーからのレスポンスに存在する場合、特定のヘッダーがNginxプロキシの動作を変更します。これらは[**ドキュメント**](https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/)で確認できます: - `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内で内部**リダイレクト**を引き起こします。したがって、**`root /`**のようなNginx設定と、ウェブサーバーからのレスポンスに**`X-Accel-Redirect: .env`**が含まれていると、Nginxは**`/.env`**の内容を送信します(パストラバーサル)。 ### **マップディレクティブのデフォルト値** **Nginxの設定**において、`map`ディレクティブはしばしば**認可制御**の役割を果たします。一般的な誤りは**デフォルト**値を指定しないことで、これが無許可のアクセスにつながる可能性があります。例えば: ```yaml http { map $uri $mappocallow { /map-poc/private 0; /map-poc/secret 0; /map-poc/public 1; } } ``` ```yaml server { location /map-poc { if ($mappocallow = 0) {return 403;} return 200 "Hello. It is private area: $mappocallow"; } } ``` `default`がない場合、**悪意のあるユーザー**は`/map-poc`内の**未定義のURI**にアクセスすることでセキュリティを回避できます。[Nginxマニュアル](https://nginx.org/en/docs/http/ngx_http_map_module.html)では、そのような問題を避けるために**デフォルト値**を設定することを推奨しています。 ### **DNSスプーフィング脆弱性** 特定の条件下でNginxに対するDNSスプーフィングは可能です。攻撃者がNginxが使用する**DNSサーバー**を知っていて、そのDNSクエリを傍受できる場合、DNSレコードをスプーフィングできます。ただし、NginxがDNS解決に**localhost (127.0.0.1)**を使用するように設定されている場合、この方法は効果がありません。Nginxでは、次のようにDNSサーバーを指定できます: ```yaml resolver 8.8.8.8; ``` ### **`proxy_pass` と `internal` ディレクティブ** **`proxy_pass`** ディレクティブは、リクエストを他のサーバーにリダイレクトするために使用されます。内部または外部のいずれかです。**`internal`** ディレクティブは、特定の場所が Nginx 内部でのみアクセス可能であることを保証します。これらのディレクティブ自体は脆弱性ではありませんが、その設定はセキュリティの欠陥を防ぐために慎重に検討する必要があります。 ## proxy_set_header Upgrade & Connection nginx サーバーが Upgrade および Connection ヘッダーを渡すように設定されている場合、[**h2c Smuggling attack**](../../pentesting-web/h2c-smuggling.md) を実行して保護された/内部エンドポイントにアクセスすることができます。 > [!CAUTION] > この脆弱性により、攻撃者は **`proxy_pass` エンドポイント (`http://backend:9999` の場合) との直接接続を確立することができます**。その内容は nginx によってチェックされません。 `/flag` を盗むための脆弱な設定の例は [こちら](https://bishopfox.com/blog/h2c-smuggling-request) です。 ``` 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_pass` が `http://backend:9999/socket.io` のような特定の **path** を指していても、接続は `http://backend:9999` に確立されるため、**その内部エンドポイント内の他のパスに連絡することができます。したがって、proxy_pass の URL にパスが指定されていても関係ありません。** ## 自分で試してみる Detectify は、この記事で説明したいくつかの誤設定を持つ脆弱な Nginx テストサーバーを Docker を使用してセットアップできる GitHub リポジトリを作成しました。自分でそれらを見つけてみてください! [https://github.com/detectify/vulnerable-nginx](https://github.com/detectify/vulnerable-nginx) ## 静的解析ツール ### [GIXY](https://github.com/yandex/gixy) Gixy は Nginx 設定を分析するツールです。Gixy の主な目的は、セキュリティの誤設定を防ぎ、欠陥の検出を自動化することです。 ### [Nginxpwner](https://github.com/stark0de/nginxpwner) Nginxpwner は、一般的な Nginx の誤設定や脆弱性を探すためのシンプルなツールです。 ## 参考文献 - [**https://blog.detectify.com/2020/11/10/common-nginx-misconfigurations/**](https://blog.detectify.com/2020/11/10/common-nginx-misconfigurations/) - [**http://blog.zorinaq.com/nginx-resolver-vulns/**](http://blog.zorinaq.com/nginx-resolver-vulns/) - [**https://github.com/yandex/gixy/issues/115**](https://github.com/yandex/gixy/issues/115) {{#include ../../banners/hacktricks-training.md}}