Skip to content

Instantly share code, notes, and snippets.

@OldBigBuddha
Created November 24, 2023 09:37
Show Gist options
  • Select an option

  • Save OldBigBuddha/2a012a1ba3a5831dfb8f4b1c8c290db2 to your computer and use it in GitHub Desktop.

Select an option

Save OldBigBuddha/2a012a1ba3a5831dfb8f4b1c8c290db2 to your computer and use it in GitHub Desktop.
セキュリティ・ミニキャンプ in 広島 2023 講義資料

nginx の設定ミスによる情報漏洩

このワークショップでは、nginx の alias 機能の設定ミスによって Alias Traversal が発生し、Web サーバー上に存在する一部の情報を窃取できる環境を体験します。今回は host.access.log というファイルををサーバー外から取得することをゴールとします。このファイルは Web サーバーへどのようなアクセスがあったかを記録するためのログファイルで、管理者以外が閲覧できてしまう状態は好ましくありません。

Step1. nginx の Docker コンテナを起動する

以下のコマンドを用いて nginx が含まれる Docker コンテナを起動してください。

$ docker run -d -p 8080:80 --name seccamp-nginx nginx:1.25.3

http://localhost:8080 へアクセスし、Welcome to nginx! が表示されていれば準備完了です。

※ すでに Docker を学校の授業などで使われている場合は 8080 番ポートがすでに使われていて、コンテナが起動しない可能性があります。もしそうであれば既存のコンテナを一度停止するか、-p 8080:80 の部分を -p 8081:80 にするなどで対応してください。よくわからない場合は遠慮なく講師やチューターに声をかけてください。

Step2. 起動した seccamp-nginx を VSCode 経由で確認する

これからはコンテナ内のファイルを閲覧・編集をします。Docker の機能を用いて操作しても良いですが、ここではファイル操作が主目的なので簡略化のため VSCode を用いてコンテナ内のファイルを閲覧・編集します。

※ 慣れている方は Docker コンテナへ直接接続し、CLI を用いて操作してもらって構いません。但し、ここからはコマンド操作が不慣れな方を想定し VSCode で Docker コンテナの中身を操作する前提で説明を行いますので、適宜お手元の環境に合わせて読み替えてください。

まずは VSCode を起動し、左下の部分(マウスをホバーすると「リモートウィンドウを開きます」と表示されます)をクリックします。すると項目を選べるようになりますので「実行中のコンテナへアタッチ…」を選択します。すると先程起動した seccamp-nginx が選択肢に表示されるので、その項目を選択します。

※ WSL 上で Docker を動かしている方は一度 VSCode で WSL へ接続した後、上記の手順を行うことで正しくコンテナを選べるようになります。

これで VSCode を経由して seccamp-nginx を操作できるようになりました。もしうまくいかない場合は講師やチューターに質問してください。

Step3. 設定ファイルを確認する

初期の画面では「ファイルを開く…」が表示されていますので、その項目をクリックし /etc/nginx/conf.d/default.conf と入力してください。以下のようなファイルが開ければ成功です。

server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;

    access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

これは nginx が最初から準備しているデフォルト状態の設定ファイルです。

設定ファイルの存在が確認できれば、最後にアクセスログを出力する設定をしておきます。最初に述べた通り、今回のワークショップではここで有効化したアクセスログをサーバー外から窃取することがゴールとなります。

アクセスログを有効にするためにはコメントアウトされている 6 行目を戻しておいてください(先頭の # を削除する)。今回は nginx デフォルトの設定に従っただけではありますが、一般的にアクセスログなどアプリケーションのログは /var/log 配下に置かれやすいことは知見として知っておくと良いでしょう。

#access_log  /var/log/nginx/host.access.log  main;

↓

access_log  /var/log/nginx/host.access.log  main;

Tips: Linux や macOS などの UNIX-like な OS では Filesystem Hierarchy Standard という標準に沿ってディレクトリやファイルを配置することが多いです。一般的にどこのディレクトリにどんな情報が含まれるかを知りたい場合は一度全文を確認すると良いかもしれません。但し、あくまで標準であって強制力のあるルールではないため運営者などによって違いが発生することは念頭に置いておきましょう。

Step4. ファイルを配信する際の設定を確認

※ nginx に関する知見を持っている方は読み飛ばしてもらって構いませんが、一部作業が発生しています。ここで行った作業の結果は後に Alias Traversal を試すときに利用します。

nginx にはいくつかの設定項目が存在しますが、ここでは今回のワークショップで重要となってくる location の部分のみ説明します。まずは先程生成された設定ファイル(/etc/nginx/conf.d/default.conf)の8行目以降の location / {} に注目してください。nginx において特定の範囲に影響を与える設定項目をディレクティブ(directive)と称し、今から確認する設定は location ディレクティブと言えます。

location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
}

これは / というパスへリクエストされた場合にサーバー上のどのファイルを配信するかを設定しています。この例では root ディレクティブが /usr/share/nginx/html となっていますので、http://location:8080/ へアクセスすると /usr/share/nginx/html を起点としてファイルを指定できるということになります。実際に /usr/share/ngix/html を確認してみましょう。

VSCode で「ファイル」→「フォルダを開く」を選択した後、先程の /usr/share/nginx/html を入力しましょう。左側のファイルツリーからファイル一覧を確認することができます。試しに index.html を開いてみると <h1>Welcome to nginx!</h1> という行があり、先程ブラウザ上でアクセスした際に表示された HTML ファイルであることが確認できます。

続いて同じ階層にファイルを作成し、意図したファイルを nginx を用いて配信できることを確認しましょう。まずはディレクトリをひとつ作成し、その配下に新しいファイルをひとつ準備します。ここでは seccamp というディレクトリを作成し、その配下に mini-2023-hiroshima.txt を作成したとします(ファイル形式は任意ですが、テキストファイルか HTML ファイルを推奨します)。ファイルにアクセスしたことがわかるように好きな文字列を書き込んでおいてください(hello world など)。

例に従った場合、次のディレクトリ構造になります。

# tree
.
|-- 50x.html
|-- index.html
`-- seccamp
    `-- mini-2023-hiroshima.txt

2 directories, 3 files

ファイルが準備できたら http://localhost:8080/seccamp/mini-2023-hiroshima.txt へアクセスし、先ほど書き込んだ任意の文字列が表示されるかを確認します。表示されていれば nginx によって /usr/share/nginx/html 配下のファイルが配信されているという挙動を確認できたことになるでしょう。

Step5. alias 機能の確認

これまでで nginx を使って Web サーバー上のファイルを配信するための基本的な方法を確認しました。次に今回の肝である alias ディレクティブの正しい設定・挙動を確認します。

先程の location の中に root ディレクティブを定義してやることで、相対的にファイルを取得できることを確認できました。これとは別に nginx では alias ディレクティブを定義することが可能です。エイリアス(alias)とは身近なもので例えると Windows や macOS のショートカットのようなものです。URL で指定しているパスとサーバー上のディレクトリ構造が一致している場合は root ディレクティブを使い、そうでない場合は alias ディレクティブを使うという分け方ができます。

例えば基本的には /usr/share/nginx/html 以下のファイルを公開したいんだけど、画像ファイル(/img/xxx にアクセスされた場合)は /var/images/ にあるものを配信したいという場合は以下の書き方をします。手元で試してみましょう。/etc/nginx/conf.d/default.conf に最初からある location / の下に次の例のような location /img/ を追記します。

location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
}

location /img/ {
    alias	/var/images/;
}

設定を書き換えたら次のコマンドで nginx へ設定を反映させます(以降のコマンドは Docker コンテナへ接続した VSCode 上で実行してください)。

# /etc/init.d/nginx reload

今の Docker コンテナには /var/images が存在しないため手動で作成します。ついでにテキストファイルを追加しておきましょう。もちろん画像ファイルを追加しても良いですが、手順簡略化のためにここではテキストファイルを用いています。

# mkdir -p /var/images
# echo "this is image data" > /var/images/sample.txt

ブラウザを用いて http://localhost:8080/img/sample.txt へアクセスすると this is image data という文字列が表示されます。

Step6. Alias Traversalを発生させる設定ミス

ここから実際に Alias Traversal を体験していきますが、その前に本来アクセスできないはずのファイルが本当にアクセスできないかを確認しておきましょう。http://localhost:8080/img../log/nginx/host.access.log へアクセスしてみましょう。Not Found が出れば現段階で host.access.log へアクセスできないことが分かりますので正解です(本来このファイルは外部から見れないはずです)。もし Not Found 以外のエラーや host.access.log がダウンロードできてしまう状態であれば設定ミスですので、チューターや講師に声をかけてください。

ここからが本題です。先程設定した location /img/ を次のように書き換えます。

location /img {
    alias	/var/images/;
}

location /imgimg の後ろに / が存在しません。たったこれだけで Alias Traversal が発生します。

まずはこの設定が正しい設定と同じ挙動をするかを確認します。設定をリロードした後、再度 http://localhost:8080/img/sample.txt へアクセスしてください。先程確認した文字列が再び表示されれば、正常な挙動が実現できていることが分かります。

では、次に http://localhost:8080/img../log/nginx/host.access.log へアクセスしてみましょう。すると先程とは違い最初に設定した nginx のアクセスログがダウンロードできます。/var/images 配下のファイルのみが配信対象となる設定をしたつもりでしたが、最後の / を忘れただけで /var 配下すべてのファイルが配信されるようになってしまいました。

以上で Alias Traversal を実践できたことになります。今回は個人の PC の環境下で、しかも nginx 以外が動いていない Docker コンテナ上で再現したためあまり面白みを感じなかったかもしれません。ですが、現実世界では nginx と同時にメールサーバーやファイルサーバーを運用することもありえます。その際には nginx 同様 /var/log 下に何らかのログを配置することがありえますし、メールサーバーに関しては /var/spool/mail/ 配下へ一時的にメールを保存することもあります。そのような状態のサーバーで今回体験した Alias Traversal が発生すればどれほどの被害を被ることになるでしょうか?

ワークショップが早めに終わった方は /var 配下に一般的にどのようなデータが配置されるのか調べてみてください。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment