[Web 安全] PHP 反序列化漏洞 —— PHP 序列化 反序列化
关注这个专栏的其他相关笔记:[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客
0x01:PHP 序列化 — Serialize
序列化就是将对象的状态信息转化为可以存储或传输的形式的过程,在 PHP 中,通常使用 serialize() 函数来完成序列化的操作。
下面笔者直接简要点列出各个数据类型序列化之后的结果,不信的崽崽可以自己跑一下:
echo serialize(null); // N;echo serialize(123); // i:123; => int 类型的值为 123echo serialize(123.3); // d:123.3; => double 类型的值为 123.3echo serialize(true); // b:1; => Boolean 类型的值为 Trueecho serialize("Blue17"); // s:6:"Blue17"; => String 类型的值为 Blue17echo serialize(array("Blue17", 17, null)); // a:3:{i:0;s:6:"Blue17";i:1;i:17;i:2;N;}
0x0101:序列化结果解析 — Array 类型
这部分笔者就简单分析一下上面 Array 类型序列化后的结果:
echo serialize(array("Blue17", 17, null)); // a:3:{i:0;s:6:"Blue17";i:1;i:17;i:2;N;}/*a:3 => array 中有三个元素i:0;s:6:"Blue17" => index 为 0 的地方是一个长度为 6 的 String 类型的元素,值为 Blue17i:1;i:17; => index 为 1 的地方是一个 int 类型的元素,值为 17i:2;N; => index 为 2 的地方是一个 Null 类型的元素。*/
0x0102:序列化结果解析 — 类
类的序列化结果基本大差不差,但是不同 “访问类型” 的变量序列化的结果有很大差异,这部分笔者就按照 “访问类型” 进行分类,并对每个单独进行讲解。
1. 类序列化结果解析 —— Public 型
这里我们开始进入正规,讲讲最常见的类的序列化结果,先来一个最常见的试试水:
<?phpclass demo {public $var1; // 这个变量没有赋值public $var2 = "Blue17"; // 这个变量赋予了字符串类型的值var $var3 = 17; // 虽然修饰符是 var 但其实还是 public 类型的function printVar($var) {$localVar = $var; // 类方法中的局部变量echo $localVar;}}// O:4:"demo":3:{s:4:"var1";N;s:4:"var2";s:6:"Blue17";s:4:"var3";i:17;}echo serialize(new demo(array(123)));
下面我们仔细分析一下结果,可以看到,它序列化的结果基本全是变量,类中方法其实是没有被序列化的,类中的局部变量也没有被序列化:
O:4:"demo":3:{s:4:"var1";N;s:4:"var2";s:6:"Blue17";s:4:"var3";i:17;}// O:4:"demo":3 => Object 对象是一个 4 字的叫 demo,其中有 3 个属性(变量)// s:4:"var1";N; => 属性名称占 4 字节,叫 var1 其值是 Null 类型// s:4:"var2";s:6:"Blue17" => 属性名称占 4 字节,叫 var2,其值是 String 类型,长度为 6 内容是 Blue17// s:4:"var3";i:17; => 属性名称占 4 字节,叫 var3,其值是 int 类型,值为 17。
2. 类序列化结果解析 —— Protected 型
下面我们来看看如果类的属性中混入了 Protect 型的变量它序列化的结果长啥样吧:
<?phpclass demo {protected $var1; // 这个变量没有赋值protected $var2 = "Blue17"; // 这个变量赋予了字符串类型的值protected $var3 = 17; // 虽然修饰符是 var 但其实还是 public 类型的}echo serialize(new demo()) . "\n";// O:4:"demo":3:{s:7:"*var1";N;s:7:"*var2";s:6:"Blue17";s:7:"*var3";i:17;}echo urlencode(serialize(new demo()));// O%3A4%3A%22demo%22%3A3%3A%7Bs%3A7%3A%22%00%2A%00var1%22%3BN%3Bs%3A7%3A%22%00%2A%00var2%22%3Bs%3A6%3A%22Blue17%22%3Bs%3A7%3A%22%00%2A%00var3%22%3Bi%3A17%3B%7D
下面我们仔细分析一下结果,这里笔者特意对它序列化后的结果做了一个编码,因为里面其实有一些不可见的字符,不编码是看不出来的,大部分内容其实与 Public 一致,但是被 protected 修饰的变量序列化后的内容就有很大不同了:
O:4:"demo":3:{s:7:"*var1";N;s:7:"*var2";s:6:"Blue17";s:7:"*var3";i:17;}// s:7:"*var1";N; => 属性名称占 7 个字节?怎么数着只有 5 个?// 这个是 URL 编码后的内容:s%3A7%3A%22%00%2A%00var1%22%3BN%3B// 这个是简单解码后的样子:s:7:"%00*%00var1";N;
如上,可以发现,Protected 属性序列化后明面看只有 *var1 这样,但其实 * 两边其实还藏了两个 ASCII 码值为 0 的字符 (这也引出后面一个 Bug,在尝试构造反序列化值得时候,不建议通过直接复制就篡改被 Protected 或者 Private 修饰的值,因为你复制得内容一般都不全,会丢掉这个特殊的 %00)。
3. 类序列化结果解析 —— Private 型
下面我们来看看如果类的属性中混入了 Private 型的变量它序列化的结果长啥样吧:
<?phpclass demo {private $var1; // 这个变量没有赋值private $var2 = "Blue17"; // 这个变量赋予了字符串类型的值}echo serialize(new demo()) . "\n";// O:4:"demo":2:{s:10:"demovar1";N;s:10:"demovar2";s:6:"Blue17";}echo urlencode(serialize(new demo()));// O%3A4%3A%22demo%22%3A2%3A%7Bs%3A10%3A%22%00demo%00var1%22%3BN%3Bs%3A10%3A%22%00demo%00var2%22%3Bs%3A6%3A%22Blue17%22%3B%7D
下面我们仔细分析一下结果,这里笔者特意对它序列化后的结果做了一个编码,因为里面其实有一些不可见的字符,不编码是看不出来的,大部分内容其实与 Public 一致,但是被 Private 修饰的变量序列化后的内容就有很大不同了:
O:4:"demo":2:{s:10:"demovar1";N;s:10:"demovar2";s:6:"Blue17";}// s:10:"demovar1";N; => 属性名称占 10 个字节?怎么数着只有 8 个?// 这个是 URL 编码后的内容:s%3A10%3A%22%00demo%00var1%22%3BN%3B// 这个是简单解码后的样子:s:10:"%00demo%00var1";N; => %00 算一位,数一数,刚好 10 位
如上,可以发现,Private 属性序列化后明面看只有 demovar1 这样,但其实类名两边其实还藏了两个 ASCII 码值为 0 的字符,所以其真实格式为(URL 编码后的哈,不编码的话 ASCII 值为 0 的其实是不可见字符) %00类名%00变量名。
0x02:PHP 反序列化 — Unserialize
以下是反序列化相关的几个特性:
-
反序列化就是将序列化得到的字符串转化为一个对象的过程。
-
反序列化生成的对象成员属性值由被反序列化的字符串决定,与原来类预定义的值无关。
-
PHP 中通过
unserialize()函数进行反序列化,序列化使用serialize()函数。 -
反序列化不触发类的成员方法,需要被调用方法后才会被触发。(不一定,这个后面讲)
下面笔者就以 Public 型的类为例,讲解一下反序列化的用处(另外两种类型,流程一致,但是要特别注意 %00 到底有没有被复制过去,如果报错了,一般就是这个的问题)。
0x0201:反序列化 —— 正常流程
首先是比较简单的 Public 型类的反序列化,我们先创建一个类,假设叫 Note (笔记)类吧,然后我们写笔记就要实例化这个类,然后往这个类的对象里写东西,代码如下:
<?phpclass Note {public $title; // 笔记标题public $content; // 笔记内容// 记录标题 & 内容function write($title, $content) {$this -> title = $title;$this -> content = $content;}// 读取标题 & 内容function read() {echo "Title: " . $this -> title . "\n";echo "Content: " . $this -> content . "\n";}
}// 实例化笔记类
$note = new Note();
// 往笔记里写东西
$note -> write("Hello, World!!", "Today Is a Nice Day!!");
如上,我们已经往笔记里写东西了,写了你要保存吧,可是你是个对象你咋保存?这时就可以使用序列化,把 $note 这个对象里的内容序列化然后存储在一个文件里:
// 保存 $note 笔记里的东西
echo serialize($note); // O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}

OK,保存了你过段时间得看吧,给你看下面这个东西你又看不懂是不是:
O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}
这个时候就需要用我们软件进行反序列化然后再调用 read 方法了是吧:
// 保存 $note 笔记里的东西
$save = serialize($note); // O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}// 查看保存的内容
$raw = unserialize($save); // 对序列化的内容进行反序列化
$raw -> read();

如上,可以看到,通过反序列化被保存的值,我们成功还原了用户笔记里写的东西。下面是整个测试的源码(这里笔者特别提醒,反序列化的环境中要有序列化的那个类,不然即使反序列化了也是无法执行类的方法的):
<?php// 创建笔记类
class Note {public $title; // 笔记标题public $content; // 笔记内容// 记录标题 & 内容function write($title, $content) {$this -> title = $title;$this -> content = $content;}// 读取标题 & 内容function read() {echo "Title: " . $this -> title . "\n";echo "Content: " . $this -> content . "\n";}
}// 实例化笔记类
$note = new Note();
// 往笔记里写东西
$note -> write("Hello, World!!", "Today Is a Nice Day!!");// 保存 $note 笔记里的东西
$save = serialize($note); // O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}// 查看保存的内容
$raw = unserialize($save); // 对序列化的内容进行反序列化
$raw -> read(); // 执行类的成员方法
0x0202:反序列化 —— 异常流程
我们继续假设,我们刚刚是本地的,假设你写了笔记,然后你要上传,那你上传服务端的序列化的内容就是下面这个:
O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:21:"Today Is a Nice Day!!";}
假设,攻击者截获了这个内容,按照 PHP 序列化的格式自己改了一下(主要是改内容和长度):
O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:25:"Today Is NOT a Nice Day!!";}
如上,我们添加了一个单词 Not ,然后修改了长度,然后我们 “发送” 到服务端,让他读一下,代码如下:
<?php// 创建笔记类
class Note {public $title; // 笔记标题public $content; // 笔记内容// 记录标题 & 内容function write($title, $content) {$this -> title = $title;$this -> content = $content;}// 读取标题 & 内容function read() {echo "Title: " . $this -> title . "\n";echo "Content: " . $this -> content . "\n";}
}
// 接收的信息
$receive = 'O:4:"Note":2:{s:5:"title";s:14:"Hello, World!!";s:7:"content";s:25:"Today Is NOT a Nice Day!!";}';$raw = unserialize($receive); // 对序列化的内容进行反序列化
$raw -> read(); // 调用读方法

如上,可以看到,结果就这样被修改了。这就是前面介绍的反序列化的一个特性 “反序列化生成的对象成员属性值由被反序列化的字符串决定,与原来类预定义的值无关。”,也是我们后面 “反序列化漏洞的依据”。
相关文章:
[Web 安全] PHP 反序列化漏洞 —— PHP 序列化 反序列化
关注这个专栏的其他相关笔记:[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客 0x01:PHP 序列化 — Serialize 序列化就是将对象的状态信息转化为可以存储或传输的形式的过程,在 PHP 中,通常使用 serialize() 函数来完成序列化的操作…...
QT入门--QMainWindow
从上向下依次是菜单栏,工具栏,铆接部件(浮动窗口),状态栏,中心部件 菜单栏 创建菜单栏 QMenuBar* mybar1 menuBar(); 将菜单栏放到窗口中 setMenuBar(mybar1); 创建菜单 QMenu *myfilemenu mybar1-…...
C++ | 高级教程 | 信号处理
👻 概念 信号 —— 操作系统传给进程的中断,会提早终止程序有些信号不能被程序捕获,有些则可以被捕获,并基于信号采取适当的动作 信号描述SIGABRT程序的异常终止,如调用 abortSIGFPE错误的算术运算,比如除…...
最新前端框架选型对比与建议(React/Vue/Svelte/Angular)
前端框架选型对比与建议(React/Vue/Svelte/Angular) 一、核心框架技术特性对比(基于最新版本) 维度React 19 25Vue 3.5 12Svelte 5 25Angular 19 5核心理念函数式编程、JSX语法、虚拟DOM渐进式框架、组合式API、模板语法编译时框…...
游戏引擎学习第123天
仓库:https://gitee.com/mrxiao_com/2d_game_3 黑板:线程同步/通信 目标是从零开始编写一个完整的游戏。我们不使用引擎,也不依赖任何库,完全自己编写游戏所需的所有代码。我们做这个节目不仅是为了教育目的,同时也是因为编程本…...
计算机网络:从底层原理到前沿应用,解锁数字世界的连接密码
计算机网络:从底层原理到前沿应用,解锁数字世界的连接密码 在信息如洪流般奔涌的时代,计算机网络宛如无形的脉络,贯穿于我们生活的每一个角落。它不仅是数据传输的通道,更是连接全球、驱动创新的核心力量。从日常的网络…...
grafana K6压测
文章目录 install and runscript.jsoptions最佳实践 report 解析 https://grafana.com/docs/k6/latest/get-started install and run install # mac brew install k6当前目录下生成压测脚本 # create file script.js k6 new [filename] # create file ‘script.js’ in …...
Vue的组合式API和选项式API有什么区别
Vue3的组合式API(Composition API)和选项式API(Options API)是两种不同的组件编写方式,主要区别如下: 1. 代码组织方式 选项式API: 按照选项(如data、methods、computed等࿰…...
ubuntu 安全策略(等保)
windows 三个帐号屏保设置组策略,密码超时次数/审计记录; linux 应具有登录失败处理功能,应配置并启用结束会话、限制非法登录次数和当登录连接超时自动退出等相关措施。 1、在系统中新建测试用户,使用此用户登录时多次输入错误密码&…...
c/c++蓝桥杯经典编程题100道(22)最短路径问题
最短路径问题 ->返回c/c蓝桥杯经典编程题100道-目录 目录 最短路径问题 一、题型解释 二、例题问题描述 三、C语言实现 解法1:Dijkstra算法(正权图,难度★★) 解法2:Bellman-Ford算法(含负权边&a…...
AI工具集合
设计相关 1. mastrtgo(暂时免费) :可以根据自然语言生成UI设计稿和前端代码 MasterGo 莫高设计 - AI 时代的数字界面生产平台 2. reddy.ai(暂时免费): 国外类似mastrtgo的平台 Readdy 3. midjourney (…...
CSDN 博客:CC++ 内存管理详解
CSDN 博客:C/C 内存管理详解 在软件开发过程中,内存管理是一个非常重要的环节。对于 C 和 C 这两种编程语言,它们都拥有独特的内存管理机制,理解这些机制对于编写高效、健壮的程序至关重要。本文将详细讲解 C/C 内存管理相关的内…...
表单制作代码,登录动画背景前端模板
炫酷动效登录页 引言 在网页设计中,按钮是用户交互的重要元素之一。一个炫酷的按钮特效不仅能提升用户体验,还能为网页增添独特的视觉吸引力。今天,我们将通过CSS来实现一个“表单制作代码,登录动画背景前端模板”。该素材呈现了数据符号排版显示出人形的动画效果,新颖有…...
嵌入式项目:STM32刷卡指纹智能门禁系统
本文详细介绍基于STM32的刷卡指纹智能门禁系统。 获取资料/指导答疑/技术交流/选题/帮助,请点链接: https://gitee.com/zengzhaorong/share_contact/blob/master/stm32.txt 1 系统功能 1.1 功能概述 本系统由STM32硬件端(下位机)…...
LeetCode 热题100 141. 环形链表
LeetCode 热题100 | 141. 环形链表 大家好,今天我们来解决一道经典的算法题——环形链表。这道题在 LeetCode 上被标记为简单难度,要求我们判断一个链表中是否存在环。下面我将详细讲解解题思路,并附上 Python 代码实现。 题目描述 给定一个…...
以绘图(绘制点、直线、圆、椭圆、多段线)为例子 通过设计模式中的命令模式实现
为了在命令模式的基础上实现撤销(Undo)和回退(Redo)功能,我们可以在每个命令类中记录一些必要的状态,允许我们撤销之前的操作,并在需要时回退操作。常见的做法是使用一个命令堆栈来存储历史命令…...
鹏哥c语言数组(初阶数组)
前言: 对应c语言视频54集 内容: 一维数组的创建 数组是一组相同元素的集合, 数组的创建方式 type_t就是数组的元素类型,const_n是一个常量表达式,用来指定数组的大小 c99标准之前的,数组的大小必须是…...
利用go-migrate实现MySQL和ClickHouse的数据库迁移
1. 背景 在使用gorm时 , 尽管已经有了自动建表和钩子函数 . 但是在面临希望了解到数据库的变更 , 和插入一些系统字段时 , 以及最关键的数据库迁移的工作 . gorm显得稍微有点不便 . 在了解到migrate这项技术后 , 就使用go-migrate开发了一个可以迁移MySQL和ClickHouse数据库的…...
计算机毕业设计SpringBoot+Vue.js企业客户管理系统(源码+LW文档+PPT+讲解+开题报告)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
jmeter 如何做移动端的测试 特别是兼容性测试
JMeter本身主要是一款用于性能测试和功能测试的工具,虽然它并非专门为移动端测试设计,但可以通过一些方式来对移动端应用进行测试,以下从测试准备、测试过程及注意事项等方面为你详细介绍: 一、测试准备 (一)环境搭建 JMeter安装与配置:确保JMeter已经正确安装在测试机…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
