PHP反序列化

​ PHP反序列化是PHP编程中的一个概念,在介绍PHP反序列化漏洞之前,先来介绍一下PHP反序列化的作用及原理,以及其可能造成的安全问题。

定义

​ 在PHP中,序列化是将对象或数据结构转换为可存储或传输的字符串格式的过程,而反序列化则是相反的操作,它将序列化后的字符串重新转换回原来的PHP对象或数据结构,以便在程序中继续使用。

作用

  • 数据存储和恢复:方便将复杂的数据结构,如对象、数组等存储到文件、数据库或通过网络传输,之后可以通过反序列化恢复数据供程序使用。比如将用户购物车中的商品信息以序列化形式存入数据库,用户下次访问时再反序列化出来展示。

  • 对象状态重建:在一些跨页面或跨请求的操作中,能够保持对象的状态。例如在一个多步骤的表单提交过程中,可将包含用户输入数据的对象序列化后存储在会话中,在后续步骤中反序列化恢复对象来继续处理。

实现方式

​ PHP提供了 unserialize() 函数来进行反序列化操作。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// 定义一个简单的类
class MyClass {
public $property = 'Hello World';
}

// 创建类的实例
$obj = new MyClass();

// 序列化对象
$serializedObj = serialize($obj);

// 反序列化对象
$unserializedObj = unserialize($serializedObj);

// 输出反序列化后对象的属性值
echo $unserializedObj->property;
?>

安全问题

​ 如果对不可信的数据进行反序列化,可能导致安全漏洞,如代码注入、远程代码执行等。攻击者可以构造恶意的序列化数据,在反序列化时执行恶意代码。例如,攻击者通过篡改提交到服务器的序列化数据,让服务器在反序列化时执行删除重要文件等危险操作。

PHP反序列化漏洞

漏洞原理

​ PHP反序列化过程中,如果对用户输入等不可信数据未进行充分验证和过滤就直接反序列化,攻击者可构造恶意序列化数据。当服务器对其反序列化时,会触发对象的魔法函数等操作,攻击者利用这些可执行任意代码、执行系统命令或获取敏感信息等。

漏洞示例

​ 以下是一个简单示例展示反序列化漏洞:

1
2
3
4
5
6
7
8
9
10
11
<?php
class Test {
public $cmd;
public function __destruct() {
system($this->cmd);
}
}
// 假设从用户输入获取序列化数据
$serialized_data = $_GET['data'];
$obj = unserialize($serialized_data);
?>

​ 攻击者可通过访问 http://example.com/test.php?data=O:4:"Test":1:{s:3:"cmd";s:10:"rm -rf /";} ,构造恶意数据删除服务器根目录下所有文件。其中,O:4:"Test":1:{s:3:"cmd";s:10:"rm -rf /";} 是对包含 cmd 属性且值为 rm -rf / 的 Test 类对象进行序列化后的结果。

1
2
3
4
5
6
7
8
9
// 针对上面的示例编写的序列化代码
class Test {
public $cmd;
}
$obj = new Test();
$obj->cmd = 'rm -rf /';
$serialized = serialize($obj);
echo $serialized; // 输出序列化
echo urlencode $serialized; // 输出url编码后的序列化

魔法函数

在PHP中,魔法函数是具有特殊名称和功能的预定义函数,它们会在特定的场景下自动被调用。通常有一些PHP的魔法函数会导致反序列化漏洞,如:

  1. __construct()
  • 作用:对象创建时自动调用。

  • 触发场景:反序列化时若对象未初始化,可能触发构造函数。

  • 风险:若构造函数中执行了未过滤的用户可控操作(如文件操作、数据库查询),攻击者可通过构造恶意对象执行任意代码。

  • 示例:

    1
    2
    3
    4
    5
    6
    class Test {
    public $file;
    public function __construct() {
    unlink($this->file); // 若$file可控,可删除任意文件
    }
    }
  1. __destruct()
  • 作用:对象销毁时自动调用。

  • 触发场景:反序列化后对象生命周期结束时触发。

  • 风险:若析构函数中调用了用户可控的系统命令或敏感操作(如 system() 、 exec() ),攻击者可直接执行恶意代码。

  • 示例:

    1
    2
    3
    4
    5
    6
    class Test {
    public $cmd;
    public function __destruct() {
    system($this->cmd); // 直接执行命令
    }
    }
  1. __wakeup()
  • 作用:反序列化时自动调用(PHP 5.6.25之前)。

  • 触发场景: unserialize() 函数执行时触发。

  • 风险:若函数中存在未过滤的用户输入处理逻辑,攻击者可通过构造恶意对象绕过安全限制。

  • 注意:PHP 5.6.25后,若对象属性数量大于实际定义的属性数量, __wakeup() 会被跳过(可被利用绕过检测)。

  1. __sleep()
  • 作用:序列化时自动调用。

  • 触发场景: serialize() 函数执行时触发。

  • 风险:若函数中执行了敏感操作(如保存数据库连接状态),攻击者可通过构造特定属性值篡改行为。

  1. __call()__callStatic()
  • 作用:调用不存在的实例方法或静态方法时触发。

  • 触发场景:反序列化后若调用未定义的方法,可触发这两个函数。

  • 风险:攻击者可通过控制方法名和参数执行任意函数(如 call_user_func() )。

  • 示例:

    1
    2
    3
    4
    5
    class Test {
    public function __call($method, $args) {
    call_user_func_array($method, $args); // 可执行任意函数
    }
    }
  1. __get() __set()
  • 作用:访问或设置不存在的属性时触发。

  • 触发场景:反序列化后若访问未定义的属性,可触发这两个函数。

  • 风险:攻击者可通过控制属性名和值读取敏感文件或执行代码(如读取 /etc/passwd )。

  1. __toString()
  • 作用:对象被当作字符串使用时触发(如拼接字符串)。

  • 触发场景:反序列化后若对象被隐式转换为字符串(如输出到页面),可触发该函数。

  • 风险:若函数中包含用户可控的输出或操作,可能导致XSS或命令执行。

  1. __invoke()
  • 作用:对象被当作函数调用时触发。
  • 触发场景:反序列化后若对象被直接调用(如 $obj() ),可触发该函数。
  • 风险:攻击者可通过构造恶意对象执行任意代码。

POP链(Property-Oriented Programming Chain)

​ 本作者现在能力有限,所以先鸽着…