[安洵杯 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["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']));
}
第一部分:
首先获取get方式传入的"f"的值,然后是一个过滤函数,将几个关键的字符串替换为空
第二部分:
这里是指如果用户没有POST传值,则SESSION这个数组会有两个默认的键值,extract()函数为从数组中将变量导入当前的符号表,这里的extract()可以进行变量覆盖,当我们POST传入SESSION["test"]=666时,此时的_SESSION数组中就只剩我们传入的值了
第三部分:
这里通过get取得img_path的值,如果没有获取到,则将'guest_img.png'进行base64编码,然后存入_SESSION数组作为'img'的值,如果获取到则对传入的值进行base64编码之后在进行sha1哈希计算存入
第四部分:
这里首先对_SESSION数组进行序列化之后再通过filter()函数过滤,然后下面对传入的'f'变量的值进行判断,根据提示,我们传入phpinfo
这里我们发现了关键的文件d0g3_f1ag.php,推测flag就在这个文件当中,要想办法读到这个文件的内容,关键的操作就在最后一个判断中,若传入show_image则对刚才序列化之后通过过滤的数据再进行反序列化,然后再读取img的内容再进行base64解码,所以这里要构造解码之后的值为d0g3_f1ag.php
解题思路
这里首先可以尝试从传入img_path的值入手,但是发现并没有对sha1逆向的操作,所以这条路走不通,那么第二条路就只能POST传入_SESSION数组的键值来构造,这里因为考虑到有过滤,所以应该使用反序列化字符逃逸来构造payload
1、键值逃逸
该方法通过在传入的键“值”上构造一个会被过滤的字符串,然后在之后的键值中构造恶意payload,这里先给出完整payload:
_SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:6:"hacker";s:4:"hack";}
我们在自己的环境之中测试,加上输出的代码
结果如下
这里的a表示数组,3表示有三个键值对,这里可以看到我们传入的flagflagflagflagflagphp被过滤了,但是php在进行反序列化的时候会严格按照前面的长度来读取,所以此时user的值就变成了";s:8:"function";s:65:",然后第二个键值就变成了我们构造的img以及d0g3_f1ag.php的base64编码。
我们payload的最后还加了;s:6:"hacker";s:4:"hack";}是因为前面的定义了整个数组的长度为3,所以这里要补上一个键值,而payload最后的花括号“}”是为了把原本的img的键值给”挤出去“,在进行反序列化的时候前面的部分已经满足了要求,所以最后的";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}就被丢弃了
2、键名逃逸
该方法原理和上面类似,不过过滤的位置变成了键名,然后在对应的值的位置构造恶意paylaod,完整的payload如下:
_SESSION[flagphp]=;s:8:"anything";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
结果如下:
此时第一个键名为";s:55:,第一个键值为任意值,注意长度不要写错就行。之后的s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";为本题固定的payload
最后的花括号}在这里是因为这次我们实际上只POST了一个键值对,程序自己有一个img的键值对,所以在进行序列化之后,前面的总长度定义为2,但是在反序列化的时候,我们恶意构造的payload已经满足了反序列化的要求,所以后面的";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}会被丢弃掉
以上方法均可读取到d0g3_f1ag.php:
然后再利用上述方法继续逃逸即可得到flag
所以本题flag为:flag{8fbcf44d-d361-41ab-b415-afc6b1532579}
标签:php,img,serialize,安洵,SESSION,2019,键值,序列化,payload From: https://www.cnblogs.com/h40vv3n/p/17725758.html