[安洵杯 2019]easy_serialize_php WP and 反序列化逃逸
[安洵杯 2019]easy_serialize_php WP and 反序列化逃逸
题目很直接,直接给了源码,代码审计
<?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);
}
$_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']));
}
$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']));
}
稍微仔细看一下,就能看到serialize和unserialize这两个函数,很大概率是序列化的题
仔细分析
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
这里将$SESSION给消掉了,重新赋了值,并且还是由post提交来赋值,说明我们可以人为修改SESSION变量
$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变量序列化,然后再用上面定义的函数来过滤关键字
'php','flag','php5','php4','fl1g'
过滤的还是很好理解的
判断语句意思是说
-
如果function是highlight_file,那又回去了,————那肯定不可能让他回去啊。。。
-
然后如果function是phpinfo的话,那么就能查看phpinfo,它提示了里面有东西,咱去看看
看到了里面有一个信息
发掘到这个信息就要看自己的积累了。。。。
这个像是我们flag所在的地方,所以一会文件包含,我们要包含这个文件
-
最后,如果function为show_image的话,我们执行操作
先反序列化,然后反序列化后的img的参数,进行base64解码,然后文件包含
这里还有个问题,如果我们提交了img参数,可是
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
无论怎么样,这段代码,在我们提交之后又将img的参数改变成我们不想要的参数,该怎么办,我们只想img参数是上面提到的那个文件
这里就涉及到一个知识点 序列化逃逸
这里我拿我自己理解的意思来说,很白话,如果不喜或不懂,可以自行百度
大概意思是:
我们应该提交两个键值对,比如test:123,img:456,可是如上面题目所言,php代码会重新赋给img一个参数sb,于是我们最后变成了
test:123,img:sb
为了我们不被骂sb,我们要456
我们可以把sb挤出去
我们只提交一个键值对test,但是,这个test后面的参数是 ;s:3:"123";s:3:"img";s:3:"456";} //先不要慌,听我慢慢说为啥这样
这样最终序列化完了就变成
O:懒得数:2:{s:4:"test";s:懒得数:";s:3:"123";s:3:"img";s:3:"456";} ";s:3:"img2";s:2:"sb";}
这样,因为本来后面php代码添加的img算是一个键值对,自己提交了一个键值对,一共俩,在执行反序列化的时候,php一看到2,
就会说 哦~,两个,正好又遇到了},就完美骗过了php,不考虑 } 后面的那些参数了,把把参数中img,当成真正的键值对,而真正键值对img2
就被挤出去了
但你会发现前面的序列化很怪,是因为多出了 ( ;s:懒得数:" )这么个玩意
那怎么办呢,所以要是序列化逃逸,需要有条件
序列化还有个特点,如果,你的参数没有达到他写的数量的时候,他会继续往后读
所以,我们需要test那里少掉一点,甚至全部消失,来让序列化往后读,一直读到我们给的第一个 ; 那里,将( ;s:懒得数:" )全部当成参数,那么我们的格式就符合序列化了,所以一般我们借助preg_replace函数,将test消掉,不过自己要计算好字符数
所以回到这题
我们提交的键值对要是能像我们上面说的那样把后面php代码给的img键值对挤掉,又要满足序列化的要求
playload: _SESSION['phpflag']=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
ZDBnM19mMWFnLnBocA==是d0g3_f1ag.php的base64加密,
然后发现源代码里有了提示
同样的方法提交,而且正好/d0g3_fllllllag base64加密后也是20字符,都不用换字符数,最后就出flag了