Session反序列化
前置知识—session
当session_start()
被调用或者php.ini
中session.auto_start
为1时,PHP内部调用会话管理器,访问用户session
被序列化后,存储到指定目录(默认为/tmp
)
存取格式有多种,常见的有如下三种,主要是前俩
处理器 | 对应的存储格式 |
---|---|
php |
键名 + 竖线 + 经过serialize() 函数序列化处理的值 |
php_serialize |
经过serialize() 函数序列化处理的数组 |
php_binary |
键名的长度对应的 ASCII 字符 + 键名 + 经过serialize() 函数反序列化处理的值 |
**漏洞产生:**写入格式和读取格式不一致
如下一句话可以有多种读取方式
php
方式存储
1 |
|
刚开始可以看到/tmp
目录下是什么都没有的
1 |
|
当以GET形式提交参数ben
后,再次查看会出现一个sess
文件,存储的是刚才提交内容,格式为
1 |
|
默认情况下是使用php
格式进行储存
php_serialize
方式存储
如果要使用这个格式进行存储,首先要进行一个声明,声明存储格式为php_serialize
1 |
|
GET提交两个参数
1 |
|
1 |
|
php_binary
方式存储
和php_serialize
方式存储一样,要先声明存储格式为php_binary
1 |
|
1 |
|
需要用二进制的方式查看
1 |
|
前面的ACK
对应的就是06
(键名benben
的长度),SOH
是01
(键名b
的长度)
PHP session反序列化漏洞
**漏洞成因:**当网站序列化并存储session
,与反序列化并读取session
的方式不同时,就可能导致session
反序列化漏洞的产生
如下两个页面,第一个页面如下,可以以php_serialize
的方式写进去进行存储
第二个页面如下,可以以php
的方式进行读取
在第二个页面可以通过序列化执行命令
1 |
|
1 |
|
在第二个页面是没有提交的地方的,但可以在第一个页面中进行GET提交参数a
1 |
|
这里可以在第一个页面进行写入,在第二个页面进行读取并执行命令
注意到这里提交写进去是以php_serialize
的方式,但是读取的时候是以php
方式进行读取的
php
的读取方式是以竖线|
为分隔符,竖线|
前面的是键名,竖线|
后面的就是要反序列化的内容
在第一个页面提交GET参数a
1 |
|
这里是以php_serialize
的方式写入的,最终存储的样子为
1 |
|
当在第二个界面以php
的方式去读取的时候
1 |
|
对于进行反序列化的部分
1 |
|
在前面的部分已经使用了;}
进行了闭合,满足了序列化数据的格式,所以后面剩余的“;}
就是无用的
所以最终会被反序列化的部分是
1 |
|
直接访问第二个页面就会得到命令执行的回显
综上,session
反序列化要满足有一个页面可以写入,还有一个页面可以读取,并且两个页面的方式是不一样的,就会出现session
反序列化
PHP session反序列化例题
题目的index.php
的源码如下
1 |
|
可以看到有个hint.php
页面,访问得到源码如下
1 |
|
审计代码
- 在
index.php
页面中,启动了一个 PHP 会话,默认是使用php
的方式,有一个Flag
的类,其中有两个属性分别是name
和her
,在发序列化时会触发__wakeup
魔术方法,先给her
赋值为一个随机的1到10000的MD5值,并和name
进行比较,条件满足就会包含flag.php
页面,并输出flag,但是在这个页面中没有输入点 - 在
hint.php
页面中,声明了session的存储方式为php_serialize
,并可以提交GET参数a
,有输入的地方
在这题中,满足了可以在hint.php
页面中写入,可以在index.php
页面中读取,并且两个页面的存储方式不一样,说明存在有session反序列化漏洞
要想读取flag,就要满足name
和her
的值相等,可以使用取地址符&
满足这个条件
1 |
|
1 |
|
在hint.php
页面提交GET参数a
1 |
|
然后直接访问index.php
页面就可以得到flag了