前置知识
首先我们要了解session是什么
session又被称为会话控制 和cookie类似 用于保持会话的
但是cookie一般是存储再客户端的 session一般是存储在服务端的
在PHP中 有一个php_session变量 用于存储session值
当PHP开始工作的时候 PHP会尝试从请求中查找一个session 并且保存在$_session变量之中
当PHP结束的时候 它会自动读取 $_SESSION 中的内容,并将其进行序列化 然后发送给会话保存管理器来进行保存
大致的工作流程可以参考这张图片![[Pasted image 20260309204919.png]]
漏洞发生点
就像我们在阅读同一句话的时候有不同的断句
比如下面这一段话
人多病少财富
可以表示出两种截然不同的意思
- 人多,病少,财富
- 人多病,少财富
在PHP中也是一样 对于数据读入和读出session 序列化内容和反序列化内容在使用不同的处理器的时候会存在不同的差异
这个处理器的设置在PHP中是通过session.serialize_handler实现的
一般存在三个处理器
php
存储格式:键名 + 竖线 + 经过serialize()函数序列化处理的值
php_serialize
存储格式:经过序列化函数serialize处理之后的数组内容
php_binary
键名长度对应的ASCII字符+键名+经过serialize()函数反序列化处理的值
漏洞演示
一般而言 session反序列化比较常见的情况是php和php_serialize两个处理器的矛盾
演示思路:
- 通过使用php_serialize的页面上传能够创建某个对象的恶意代码
- 通过使用php处理器的页面反序列化上一步中被污染的session文件 实现对象的创建
我们构造三个页面来完成这个漏洞的演示
我们使用小皮面板进行本地测试
首先打开小皮的www目录在其中创建文件夹session_test
我们需要一个文件 这个文件定义了一个类 并且通过__destruct魔术方法 使得当我们成功通过后续的payload创建对象的时候有这么一个类能够让我们创建
1 | <?php |
接下来我们需要两个页面
一个页面负责接收我们的payload 模拟的是CTF中存在注入点的页面 我们可以通过这个页面上传我们的恶意代码
1 | <?php |
另一个页面负责反序列化我们的session内容 注意 这里要使用和前一个页面不同的session反序列化处理器 模拟这个漏洞的产生情况(处理器差异导致的注入攻击)
![[屏幕截图 2026-03-10 001213.png]]
为了方便起见 我们直接在本地的VS上面输出一下TestClass序列化之后的内容 复制序列化之后的内容 并在前面加上php处理器的标志符号 |
构建payload如下payload=|O:9:"TestClass":0:{}
![[屏幕截图 2026-03-10 001702.png]]
此时我们去到保存路径处查看一下我们的上传内容![[屏幕截图 2026-03-10 001812.png]]
可以看到 我们上传了这么一个内容 在php_serialize处理器下 这是合法的 但是在php处理器下 由于会将竖线符号前后内容分别视作键名和键值 同时由于键名是可以任意的 真正反序列化的内容实际上是O:9:"TestClass":0:{}";}
我们看到 这个反序列化内容实际上是由两部分组成
- `O:9:”TestClass”:0:{}”
- ;}
反序列化器从O:9:”TestClass”:0:{}开始解析,成功读取一个完整的TestClass对象(因为对象序列化以}结束),然后指针停留在}` 之后。
剩余字符 ";} 不会被包含在对象反序列化过程中,因此对象被成功创建
我们查看一下另一个页面![[屏幕截图 2026-03-10 003550.png]]
没毛了
这只是简单的创建对象的操作 在实际应用中 我们可以插入RCE内容 进而实现攻击 类似操作类比之前反序列化中的内容即可
至此 漏洞演示完毕