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了
