[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() 函数来完成序列化的操作…...
汽车免拆诊断案例 | 保时捷车发动机偶发熄火故障 2 例
案例1 2008款保时捷卡宴车行驶中发动机偶发熄火 故障现象 一辆2008款保时捷卡宴车,搭载4.8 L 自然吸气发动机,累计行驶里程约为21万km。车主反映,该车行驶中发动机偶发熄火;重新起动,发动机能够起动着机ÿ…...
Python游戏编程之赛车游戏6-2
3.2 move()方法的定义 Player类的move()方法用于玩家控制汽车左右移动,当玩家点击键盘上的左右按键时,汽车会相应地进行左右移动。 move()方法的代码如图7所示。 图7 move()方法的代码 其中,第20行代码通过pygame.key.get_pressed()函数获…...
数据安全_笔记系列09_人工智能(AI)与机器学习(ML)在数据安全中的深度应用
数据安全_笔记系列09_人工智能(AI)与机器学习(ML)在数据安全中的深度应用 人工智能与机器学习技术通过自动化、智能化的数据分析,显著提升了数据分类、威胁检测的精度与效率,尤其在处理非结构化数据、复杂…...
渗透测试【order by盲注实践】
实践环境基于sqli-lab靶场的第46关进行 bool盲注 代码如下: import requests from bs4 import BeautifulSoup# 定义获取用户名的函数,使用 BeautifulSoup 解析 HTML 页面,提取用户名信息 def get_username(resp):soup BeautifulSoup(resp,…...
ROS的action通信——实现阶乘运算(三)
在ROS中除了常见的话题(topic)通信、服务(server)通信等方式,还有action通信这一方式,由于可以实时反馈任务完成情况,该通信方式被广泛运用于机器人导航等任务中。本文将通过三个小节的分享,实现基于action通信的阶乘运…...
007:Cesium.ScreenSpaceEventHandler 知识详解,示例代码
查看本专栏目录 - 本文是第 007个API内容详解 vue+cesium 示例教程200+目录 文章目录 一、ScreenSpaceEventHandler 的基本概念初始化 ScreenSpaceEventHandler二、注册事件**常见事件类型**三、注销事件四、示例代码:鼠标移动时显示坐标信息五、示例代码:鼠标左键点击拾取地…...
期权帮|股指期货基差和价差有什么区别?
锦鲤三三每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 股指期货基差和价差有什么区别? 一、股指期货基差 股指期货基差是指股指期货价格与其对应的现货指数价格之间的差额。 股指期货基差计算公式:基差 现…...
WebSocketHandler 是 Spring Framework 中用于处理 WebSocket 通信的接口
WebSocketHandler 是 Spring Framework 中用于处理 WebSocket 通信的接口,其主要作用是定义了如何处理 WebSocket 的各种事件和消息。以下是 WebSocketHandler 的主要作用和功能: ### 1. 处理 WebSocket 生命周期事件 WebSocketHandler 定义了多个方法来…...
内网渗透测试-Vulnerable Docker靶场
靶场来源: Vulnerable Docker: 1 ~ VulnHub 描述:Down By The Docker 有没有想过在容器中玩 docker 错误配置、权限提升等? 下载此 VM,拿出您的渗透测试帽并开始使用 我们有 2 种模式: - HARD:这需要您将 d…...
一键导出数据库表到Excel
工作中,我们经常需要将数据库表导出到Excel,通常我们会用数据库编辑器之类的工具提供的导出功能来导出,但是它们的导出功能通常都比较简单。 这篇文章将介绍一种简单易用并且功能强大的导出方法。 新增导出 打开的卢导表工具,新…...
2025年电气工程与智能系统国际学术会议(IC2EIS 2025)
重要信息 官网:www.ic2eis.org(点击了解参会投稿等) 时间:2025年3月14-16日 地点:中国河南省郑州市 简介 2025年电气工程与智能系统国际学术会议(IC2EIS 2025)将于2025年3月14-16日在中国郑州举行。会议旨在为电气…...
Activiti 5 + Spring Boot全流程开发指南
目录 一、环境搭建(Spring Boot 2.x) 1.1 依赖配置 1.2 配置文件 二、流程定义与部署 2.1 创建BPMN文件(leave.bpmn) 2.2 流程部署服务 三、流程操作核心实现 3.1 启动流程实例 3.2 查询待办任务 四、审批流程处理 4.1 …...
docker安装etcd:docker离线安装etcd、docker在线安装etcd、etcd镜像下载、etcd配置详解、etcd常用命令、安装常见问题总结
官方网站 官方网址:etcd 二进制包下载:Install | etcd GitHub社区项目:etcd-io GitHub GitHub社区项目版本历史:Releases etcd-io/etcd GitHub 一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令…...
【云安全】云原生-Docker(六)Docker API 未授权访问
Docker API 未授权访问 是一个非常严重的安全漏洞,可能导致严重的安全风险。 什么是 Docker API ? Docker API 是 Docker 容器平台提供的一组 RESTful API,用于与 Docker 守护程序进行通信和管理 Docker 容器。通过 Docker API,…...
【人工智能顶刊合集】CCF-A/B/C类推荐所有期刊目录,中科院1区审稿极速,81天录用!
本期盘点【人工智能】领域CCF-A/B/C类中科院1-2区期刊最新影响因子、分区、审稿周期参考! CCF-A类 Artificial Intelligence • 影响因子:5.1 • 期刊分区:JCR1区,中科院2区 • 年发文量:126 • 自引率࿱…...
C#实战:基于腾讯云大模型知识引擎原子能力提供的文档解析API快速提取图片信息为MD文档
目录 一、大模型知识引擎 LKE介绍 1.1 如何开通服务? 1.2 大模型知识引擎组成 二、案例实战 2.1、创建项目 2.2、引入腾讯大模型知识引擎 LKE调用SDK依赖库 2.3、代码编写 2.4、界面设计 三、总结 今天借助腾讯云大模型知识引擎原子能力提供的文档解析API快速提取图片…...
第7章_将应用程序与 Keycloak 集成
将应用程序与 Keycloak 集成 到目前为止,您已经了解了 Keycloak 中的主要概念和配置选项。在本章中,您将学习如何应用它们,以便您可以配置您的应用程序并将它们与 Keycloak 集成。 通过选定的集成场景和编码示例,您将根据应用程…...
kotlin的lateinit声明 的变量可能为null吗?
kotlin的lateinit声明 的变量可能为null吗? DeepSeek R1 思考 26 秒 思考过程 嗯,用户问的是Kotlin中的lateinit变量是否可能为null。首先,我需要回顾一下lateinit的作用和特性。lateinit用于延迟初始化一个非空类型的变量,这样…...
千峰React:案例一
做这个案例捏 因为需要用到样式,所以创建一个样式文件: //29_实战.module.css .active{text-decoration:line-through } 然后创建jsx文件,修改main文件:导入Todos,写入Todos组件 import { StrictMode } from react …...
ChatGPT入驻Safari,AI搜索时代加速到来
2月25日,人工智能领域巨头OpenAI宣布了一项重磅更新:为其广受欢迎的ChatGPT应用新增Safari浏览器扩展功能,并支持用户将ChatGPT设置为Safari地址栏的默认搜索引擎。这一举措标志着OpenAI在将ChatGPT整合进用户日常网络浏览体验方面迈出了重要…...
协方差(Covariance)与得分函数:从Fisher信息矩阵看统计关联
协方差与得分函数:从Fisher信息矩阵看统计关联 协方差(Covariance)是统计学中一个基础但强大的概念,它描述了两个随机变量之间的关系。在Fisher信息矩阵中,协方差以一种特别的形式出现:得分函数的协方差。…...
【错误记录】Arrays.asList 的坑
文章目录 概要原因小结 概要 最近在写一个需求的时候用到了这个方法生成一个 List,接着再往里面添加数据的时候就报错了,比如下面的例子。 public class Main {public static void main(String[] args) {List<Integer> res Arrays.asList(1, 2,…...
JConsole远程连接错误解决
个人博客地址:JConsole远程连接错误解决 | 一张假钞的真实世界 程序启动命令及参数如下: $ java -Dcom.sent.jmxremote.sslfalse -jar math-game.jar 防火墙已经放开30000端口访问,如下: $ telnet 192.168.72.156 30000 Tryin…...
CineMaster: 用于电影文本到视频生成的 3D 感知且可控的框架。
CineMaster是一种 3D 感知且可控的文本到视频生成方法允许用户在 3D 空间中联合操纵物体和相机,以创作高质量的电影视频。 相关链接 论文:cinemaster-dev.github.io 论文介绍 CineMaster是一种用于 3D 感知和可控文本到视频生成的新型框架。目标是让用…...
解决后端跨域问题
目录 一、什么是跨域问题? 1、跨域问题的定义 2、举例 3、为什么会有跨域问题的存在? 二、解决跨域问题 1、新建配置类 2、编写代码 三、结语 一、什么是跨域问题? 1、跨域问题的定义 跨域问题(Cross-Origin Resource Sh…...
防爆手机科普:与普通手机的区别?在危险作业场景扮演什么角色?
在易燃易爆的工业环境中,如石油化工、矿山开采等领域,一款具备特殊安全性能的通讯工具显得尤为重要。这就是我们今天要深入探讨的主题——防爆手机。那么,什么是防爆手机?它与普通手机有何区别?防爆手机在这些危险作业…...
12.MySQL版题目设计|创建用户并赋权|MySQLWorkbench创建表结构|测试录题功能(mysql)
在数据库中设计可以远程登陆的MySQL用户,并给他赋权 oj_client设计表结构 数据库:oj, 表:oj_questions开始编码 连接访问数据库 创建用户并赋权 mysql -uroot -p进入mysql use mysql;select User, Host from user;create user…...
鸿蒙-状态管理V2其他方法
文章目录 前言makeObservedgetTargetObservedV2的类实例不支持JSON.stringify序列化双向绑定 前言 除了之前介绍的状态管理装饰器,在 V2 中还有其他新增的方法。 makeObserved 开发过程中我们经常会用到从服务器获取到数据(一般是 json 字符串),转化为…...
Linux基础30-C语言篇之冒泡排序【入门级】
数组的典型应用:冒泡排序 向后冒泡 思想: 一次只排好一个数,针对n个数,最差情况需要n-1次就可以排好每次排序将相邻数据两两比较,将较大或者较小的数据向后交换,等所有数据比较完成,较大或者较…...
