php反序列化字符串逃逸

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

在讨论这个前得先理解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 "<br>"; var_dump(unserialize($b)); ?> 

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

替换后长度变长。

<?php 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.'<br>';     var_dump(unserialize($str1));     echo '<br>';     $str2 = safe($str1);     echo $str2.'<br>';     var_dump(unserialize($str2));     echo '<br>'; 

上面的源码中score不可控,利用逃逸我们可以控制他的值。
上面的safe函数将get获取的name参数中关键字k替换成no,则每传入一个k长度加长1。
用get获取name参数
?name=fmyyykkkkkkkkkkkkkkkkkkkkkkkkk";s:5:"score";s:3:"888";}
结果:
php反序列化字符串逃逸
可以看到,替换前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就会被我们控制。

替换后变长度减少。

差不多的原理

<?php 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.'<br>'; var_dump(unserialize($str1)); echo '<br>'; $str2 = safe($str1); echo $str2.'<br>'; var_dump(unserialize($str2)); echo '<br>'; 

上面的源码中,User类中有name,age,score三个属性,name和age是我们可控的,score不可控,我们要利用逃逸控制score的值。
传入参数
?name=kkkkkkkkkkkkkkkkkkkkkkk&age=12345";s:5:"score";s:3:"888";s:2:"ab";s:2:"sb";}
php反序列化字符串逃逸
可以看到,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。

总结

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

版权声明:玥玥 发表于 2021-04-20 0:42:38。
转载请注明:php反序列化字符串逃逸 | 女黑客导航