PHP serialization string escape

前言

​ 这两天做了几道关于PHP反序列化字符逃逸的题目,在自己的服务器上也复现了一下,就想着写个笔记来记录一下,可以以后翻着看看。

做了几道这种类型的题总结了下共同点:

1.php序列化后的字符串经过了替换或者修改,导致字符串长度发生变化.

2.总是先进行序列化,再进行替换修改操作.

经典题目:

  1. [0CTF 2016]piapiapia (替换变长)

  2. [安洵杯 2019]easy_serialize_php (替换变短)

    文章地址:https://cbatl.gitee.io/2020/05/09/duxiangtaoyi/

漏洞浅析

替换修改之后导致序列化字符串长度变长

​ 修改代码的原理就是在传入参数的时候将短字符替换为长的字符,然后进行序列化,导致序列化后的字符变长。下面写一个test代码,来进行实验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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

1
name=phpphpphpphpphpphpphpphp&sign=hello";s:4:"sign";s:4:"ly0n";s:6:"number";s:4:"2000";}