这两天做了几道关于PHP反序列化字符逃逸的题目,在自己的服务器上也复现了一下,就想着写个笔记来记录一下,可以以后翻着看看。
做了几道这种类型的题总结了下共同点:
1.php序列化后的字符串经过了替换或者修改,导致字符串长度发生变化.
2.总是先进行序列化,再进行替换修改操作.
经典题目:
修改代码的原理就是在传入参数的时候将短字符替换为长的字符,然后进行序列化,导致序列化后的字符变长。下面写一个test代码,来进行实验。
<?php
function test($str){
return preg_replace('/x/','ha',$str);
}
$name = $_GET[name];
$sign = 'hello ly0n';
$user = array($name,$sign);
$user_ser = test(serialize($user));
echo $user_ser.'<br>';
$fake = unserialize($user_ser);
echo $fake[0].'<br>';
echo $fake[1].'<br>';
?>
然后访问可以看到
代码很简单,输入name的值,并和sign一同传入到user数组中,user数组序列化后的字符串经过test函数检测之后,输出反序列化之后的结果.
通过代码的检测我们可以了解到,序列化之后要经过test函数检测,然后才会输出结果,可以看到我们这里的代码是将x 替换为ha,我们传入name为ly0nxxx看下结果。
我们可以看到经过test函数之后x都被替换成ha了,从而导致反序列化失败,无法输出结果.利用这个漏洞,就可以对sign的值进行修改.输入name=evALxxxxxxxxxxxxxxxxxxxxxxxxxxxxx”;i:1;s:14:”hello hackerrr”;}
发现成功的到执行!sign的值被成功修改。
1.经过test函数,将x替换为了ha.将一个字符变为了两个字符,导致字符长度过长膨胀。
2.在这个例子中把sign替换为”hello ly0nly0n”,这个字符串在本实验的序列化结果是i:1;s:14:”hello ly0nly0n,由于要闭合name的双引号以及结束的花括号,所以payload应该是”;i:1;s:14:”hello ly0nly0n”;} 这里特别要注意的一点是,我们替换的sign长度必须和原来的保持一致!
3.溢出的部分成功逃逸,经过双引号闭合name,以及闭合结束时的花括号,导致sign被成功修改.
通过函数来将一些敏感字符替换为空,导致序列化字符串长度变短,之后在输出序列化后的结果。
写代码来进行测试:
传入name=ly0n sign=hello
这样一来我们就需要通过修改name 和sign 来达到修改number的目的
通过我们的代码str_rep函数内容可以看到,将php|test转换为空了,所以我们可以通过输入Name使其转换为空来使得序列化的字符串长度变短,因为在序列化中如果传入的参数被替换为空,会向后找相应字段的字符来替补,我们就可以利用这个特性,来实现字符逃逸。
可以构造这样一个payload
name=phpphpphpphpphpphpphpphp&sign=hello";s:4:"sign";s:4:"ly0n";s:6:"number";s:4:"2000";}