概述

        PHP序列化是把变量转换成可存储或传输的字符串的过程,反序列化则是把这个字符串恢复成原来的PHP常量,与JAVA序列化类似。它在保存会话、缓存数据或在不同系统间传递复杂结构时很有用。主要依靠serialize函数unserialize函数

用法示例

$data = ['name' => 'Alice', 'age' => 28, 'score' => 9.5];
$str  = serialize($data);   // 序列化
$back = unserialize($str);  // 反序列化
echo $str;
// a:3:{s:4:"name";s:5:"Alice";s:3:"age";i:28;s:5:"score";d:9.5;}

序列化格式

        PHP序列化中,不同类型有固定对应的标记

类型 格式示例
NULL N;
布尔 b:1;或b:0;
整数 i:28;
浮点 d:0.1;
字符串 s:3:"Ark"(长度为3的字符串"Ark")
数组 a:2:{i:0;s:3:"Ark";b:1;......}

        对象序列化格式实例如下:

class User {
    public $name = 'Bob';
    protected $email = 'bob@example.com';
    private $password = 'secret';
}
echo serialize(new User);
// O:4:"User":3:{s:4:"name";s:3:"Bob";s:8:"\0*\0email";s:16:"bob@example.com";s:13:"\0User\0password";s:6:"secret";}

        对于public属性,直接写属性名;对于protected属性,属性名前加"\0*\0";对于private属性,属性名前加"\0类名\0"

PHP反序列化漏洞

        魔术方法

        魔术方法(Magic Methods)是 PHP 中一类特殊的方法,它们由 PHP 引擎在特定的“魔法时刻”自动调用,而不需要开发者手动去触发。可以理解成事件钩子。

        POP链

        全称是 Property-Oriented Programming,就是从反序列化入口开始,通过一系列魔术方法的接力调用,最终触达危险函数的路径。

        简单的POP链示例如:

class Start {
    public $obj;
    function __destruct() {
        // 销毁时调用某个对象的 close() 方法
        $this->obj->close();
    }
}

class Evil {
    public $command;
    function close() {
        // 这里执行系统命令
        system($this->command);
    }
}

// 攻击者构造的 payload 序列化字符串:
// O:5:"Start":1:{s:3:"obj";O:4:"Evil":1:{s:7:"command";s:2:"id";}}
$payload = $_GET['data'];
unserialize($payload);  // 脚本结束时触发 __destruct(),执行 system("id")

        漏洞成因

        当unserialize函数的参数可以被攻击者控制的时候,攻击者就可以构造一个恶意的序列化字符串,在反序列化时便会自动触发对象里的魔术方法,最终形成一条攻击链(叫POP链),实现任意代码执行、文件操作等破坏

        即使代码里面从来没有正常创建过危险的类,但只要类定义存在,攻击者就可以在序列化字符串中指明这个类,反序列化时PHP就会重建它的对象,并自动调用魔术方法。

        必要条件

        - unserialize() 的参数是用户可控制的

        - 代码中存在至少一个类,其中魔术方法内存在可能被利用的危险操作

        - 危险操作调用了类似system()等敏感函数,且参数可以间接被控制

其他利用技巧

        利用protected/private属性的空字符

        序列化字符串中,protected 属性会带有"\0*\0",private 属性带有"\0类名\0"。这些空字符在 URL 或手工构造 payload 时可能需要编码处理。攻击者会利用这一特性来精确控制属性的可见性,绕过某些浅层的检查。

        绕过__wakeup

        在某些旧版本(PHP 5 < 5.6.25, PHP 7 < 7.0.10)中,当序列化字符串里对象属性的个数大于实际属性个数时,__wakeup()不会被调用

        O:4:"User":2:{s:3:"age";i:20;}  // 实际属性数是1,但写成了2,可绕过 __wakeup

        PHAR反序列化

        即使代码里没有unserialize(),使用phar://伪协议操作文件时,也会自动触发反序列化。当文件系统操作被执行时,PHP 会解析 PHAR 文件的元数据并反序列化,元数据可以是攻击者精心构造的 payload。

        Session反序列化

        如果应用将序列化数据存储在 Session 中,并且修改了session.serialize_handler,攻击者可能通过巧妙污染 Session 数据,使unserialize()调用时处理混合了不同处理器的恶意串,从而注入对象。

更多推荐