nginxの設定ミスで起こる脆弱性シリーズ試してみた。


はじめに

@no1zy_sec氏がnginxの設定ミスで起こる脆弱性の翻訳記事を先日公開しました。
検証する機会があったため、再現できる検証環境と確認手順をまとめました。

各脆弱性に関する詳細や対策については、翻訳記事または原文をご確認ください。

検証できる脆弱性一覧

以下の脆弱性を検証することができます。

原文

Hostヘッダフォージェリに関しては追加の記事が公開されております。
Hostヘッダフォージェリの対策について

事前準備

Docker環境の準備

本検証を実施する場合には、以下の環境を用意してください。

  • dockerが動くこと
  • nginxイメージがインストールされていること(無くても自動的にインストールします)
  • docker-composerコマンドが動作すること

また一部の検証で、リクエスト改ざんのためにローカルプロキシツールが必要になります。
本ページでは、Burp proxy(free)を使用しています。

VMの設定

筆者の環境はWindows上のVMにDockerが動く環境を用意して検証しましたが、Dockerが動けばVMの設定は不要のため、ご自身の環境に適宜置き換えてください。

Docker環境がVMにある場合、VMのネットワーク設定に以下の設定を入れてください。

[host]127.0.0.1:80 <=>[guest]0.0.0.0:8080

検証環境の立ち上げ方

以下のリポジトリをクローンしてください。
https://github.com/wild0ni0n/badnginx

クローンしたリポジトリのディレクトリに移動し、以下のコマンドを実行してください。

docker-compose up

hosts設定

hostsファイルに以下の設定を追加してください。burpの場合、[Project Options]->[Connections]->[Hostname Resolution]に設定することで、hostsをいじらなくても済みます。

アクセス

ブラウザでhttp://www.badnginx.com にアクセスし、ページが表示されれば完了です。

#1 HTTP splitting

  1. ブラウザに搭載されているデベロッパーツールを開き、リクエスト/レスポンスボディが確認できるようにしてください。

  2. Case 1-1~1-6にアクセスしてください。この時、脆弱性がある場合はレスポンスヘッダに任意のヘッダが挿入されていることが確認できます。

以下はChromeのデベロッパーツールで確認した画像です。

Case 1-1アクセス時

Case 1-4アクセス時

#2 alias パストラバーサル

  1. Case 2-1,2-2にアクセスしてください。このケースでは、確認用のための画像が表示されるだけです。

  2. Case 2-3,2-4にアクセスしてください。Cae2-3は脆弱性が存在するため、シークレットファイルが閲覧できてしまいます。

#3 SSRF

  1. Case 3-1はinternal設定がついていない設定です。リクエスト時のパスがproxy_passの値に渡されるため、サーバに対して任意のリクエストを強制させることができます。

  2. Case 3-2はinternal設定がついています。そのため、直接アクセスしてもエラーが返ってきます。

  3. Case 3-3は内部でrewriteによる書き換えを行った後(内部リクエスト)、Case 3-2と同じlocationディレクティブの処理を行います。内部リクエストが行われているため、internal設定を抜けてSSRFを悪用できる状態となっています。

Case 3のデモは、serverディレクティブを分けているだけですが、resolverに8.8.8.8を指定しているため、任意の公開ホストにSSRFさせることもできます。

#4 レスポンスヘッダの出力不備

  1. nginxのコンフィグを見てください。本脆弱性に関係するコンフィグはbadconfig.templateの91行目から105行目です。


    94行目で X-Frame-Option Denyの設定をしているのが確認できます。
    次に二つのlocationディレクティブがありますが、/ah/new-headers/のほうは、locationディレクティブの中で更にadd_headerしていることがわかります。

  2. ブラウザでCase 4-1,4-2にアクセスし、それぞれのレスポンスヘッダを確認してください。

    Case 4-1アクセス時

    X-Frame-Options Denyの設定が有効になっています。

    Case 4-2アクセス時

  3. X-Frame-Options Denyの設定はなく、Pragmaヘッダのみ追加されていることがわかりました。

脆弱性というより、add_headerの仕様ですが、add_headerを指定した階層よりも子の階層でadd_header設定が無い場合、親階層にあるadd_headerが適用されることがわかりました。

#5 Multiline response headers

CSPヘッダのような長いレスポンスヘッダでも、改行せず単一行で設定しましょうというものです。
最新バージョンのブラウザでは、複数行で返すレスポンスヘッダもブラウザ側で解釈できているようです。(確認したブラウザChrome,firefox,ie,edge)
そのため、複数行になっているか確認するためには、ローカルプロキシツールを利用して素のレスポンスヘッダを見てください。

  1. ローカルプロキシを通した状態で、Case 5-1,5-2にアクセスしてください。

    Case 5-1アクセス時のレスポンスデータ(burp)

    CSPヘッダが複数行で返していることがわかります。

    Case 5-2アクセス時のレスポンスデータ(burp)

    CSPヘッダ単一行で返していることがわかります。

#6 referer/origin検証の問題

http://www.badnginx.com のトップページのテキストボックスは、別ページからXHRでデータを取得しています。

このデモでは、badconfig.templateに以下の設定を行い、http://www.badnginx.comhttp://badnginx.com 以外からのリソース読み込みを拒否しています。


location /api/ {
  if ($http_origin ~* ((^http://www\.badnginx\.com)|(^http://badnginx\.com))) {
    add_header 'Access-Control-Allow-Origin' $http_origin;
  }

  root /var/www/;
}

Burpを通している場合、証明書のエラーによりjqueryが読み込めない場合があります。

その場合は、以下のサイトを参考に証明書の設定を行ってください。

02 HTTP通信の中身を覗いてみよう!Burp Suite Free Edition 1.6編【後編】
03 いろいろなWebブラウザでHTTPプロキシを使ってみよう!

  1. Case 6-1は同一オリジンのため、問題なく読み込めます。

  2. Case 6-2はevilnginx.comで別オリジンからリソースを読み込もうとしているため、SOPエラーが発生します。

    Case 6-3も別のオリジンですが、脆弱性があるため読み込めていることが確認できます。

※たまにキャッシュが悪さするので、表示されないときはキャッシュ消してリロードしてください。

#7 リファラの検証不備

リファラを利用した画像へのアクセス制限をしています。

  1. Case 7-1にアクセスしてください。Case 6-3でも使用したページですが、今回は画像リンクをクリックしてください。403エラーを返し、画像はみれません。

  2. もう一度同じように画像リンクをクリックし、ローカルプロキシツールでrefererヘッダを削除してください。

    選択している部分を削除して送信してください。
    そうすると美味しそうな玉葱の画像が見えました。

#8 Hostヘッダフォージェリ

Case 8はproxy_set_headerに\$http_hostを使用しているケースと\$hostを使用しているケースを設定しています。
両方ともバックエンドのbadnginx.localに渡った後、\$http_host,\$hostを返します。

  1. Case 8-1は普通にアクセスしているだけです。
  2. Case 8-2はローカルプロキシツールでHostヘッダ(以下の選択部分)をevilenginx.comに改ざんしてください。

    http_host, hostとも書き換えたevilenginx.comになっていることがわかります。

  3. Case 8-3はリクエストラインにURLを記載し、Hostヘッダを書き換えます、

    http_host, hostとも書き換えたevilenginx.comになっていることがわかります。

次に\$hostの場合の挙動を確認します。
4. Case 8-4は普通にアクセスしているだけです。

  1. Case 8-5はHostヘッダをevilenginx.comに改ざんします。http_host, hostとも書き換えたevilenginx.comになっていることがわかります。

  2. Case 8-6はリクエストラインにURLを記載し、Hostヘッダを書き換えます。
    Case 8-3とは異なり、http_host,hostの値がHostヘッダの値ではなく、リクエストラインの値になっていることが確認できます。
    これは、原文にも記載されていますが\$hostが読みに行く順番が関係しています。

    \$http_host,\$hostどちらを使用しても、攻撃者が書き換えたhostの値を送信することができました。

Hostヘッダフォージェリの対策については、追加の記事が公開されています。併せてご確認ください。
Hostヘッダフォージェリの対策について