当前位置: 首页 > news >正文

[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 型

这里我们开始进入正规,讲讲最常见的类的序列化结果,先来一个最常见的试试水:

 <?php​class 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 型的变量它序列化的结果长啥样吧:

 <?php​class 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 型的变量它序列化的结果长啥样吧:

 <?php​class 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 序列化 反序列化

关注这个专栏的其他相关笔记&#xff1a;[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客 0x01&#xff1a;PHP 序列化 — Serialize 序列化就是将对象的状态信息转化为可以存储或传输的形式的过程&#xff0c;在 PHP 中&#xff0c;通常使用 serialize() 函数来完成序列化的操作…...

QT入门--QMainWindow

从上向下依次是菜单栏&#xff0c;工具栏&#xff0c;铆接部件&#xff08;浮动窗口&#xff09;&#xff0c;状态栏&#xff0c;中心部件 菜单栏 创建菜单栏 QMenuBar* mybar1 menuBar(); 将菜单栏放到窗口中 setMenuBar(mybar1); 创建菜单 QMenu *myfilemenu mybar1-…...

C++ | 高级教程 | 信号处理

&#x1f47b; 概念 信号 —— 操作系统传给进程的中断&#xff0c;会提早终止程序有些信号不能被程序捕获&#xff0c;有些则可以被捕获&#xff0c;并基于信号采取适当的动作 信号描述SIGABRT程序的异常终止&#xff0c;如调用 abortSIGFPE错误的算术运算&#xff0c;比如除…...

最新前端框架选型对比与建议(React/Vue/Svelte/Angular)

前端框架选型对比与建议&#xff08;React/Vue/Svelte/Angular&#xff09; 一、核心框架技术特性对比&#xff08;基于最新版本&#xff09; 维度React 19 25Vue 3.5 12Svelte 5 25Angular 19 5核心理念函数式编程、JSX语法、虚拟DOM渐进式框架、组合式API、模板语法编译时框…...

游戏引擎学习第123天

仓库:https://gitee.com/mrxiao_com/2d_game_3 黑板&#xff1a;线程同步/通信 目标是从零开始编写一个完整的游戏。我们不使用引擎&#xff0c;也不依赖任何库&#xff0c;完全自己编写游戏所需的所有代码。我们做这个节目不仅是为了教育目的&#xff0c;同时也是因为编程本…...

计算机网络:从底层原理到前沿应用,解锁数字世界的连接密码

计算机网络&#xff1a;从底层原理到前沿应用&#xff0c;解锁数字世界的连接密码 在信息如洪流般奔涌的时代&#xff0c;计算机网络宛如无形的脉络&#xff0c;贯穿于我们生活的每一个角落。它不仅是数据传输的通道&#xff0c;更是连接全球、驱动创新的核心力量。从日常的网络…...

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&#xff08;Composition API&#xff09;和选项式API&#xff08;Options API&#xff09;是两种不同的组件编写方式&#xff0c;主要区别如下&#xff1a; 1. 代码组织方式 选项式API&#xff1a; 按照选项&#xff08;如data、methods、computed等&#xff0…...

ubuntu 安全策略(等保)

windows 三个帐号屏保设置组策略,密码超时次数/审计记录&#xff1b; linux 应具有登录失败处理功能&#xff0c;应配置并启用结束会话、限制非法登录次数和当登录连接超时自动退出等相关措施。 1、在系统中新建测试用户&#xff0c;使用此用户登录时多次输入错误密码&…...

c/c++蓝桥杯经典编程题100道(22)最短路径问题

最短路径问题 ->返回c/c蓝桥杯经典编程题100道-目录 目录 最短路径问题 一、题型解释 二、例题问题描述 三、C语言实现 解法1&#xff1a;Dijkstra算法&#xff08;正权图&#xff0c;难度★★&#xff09; 解法2&#xff1a;Bellman-Ford算法&#xff08;含负权边&a…...

AI工具集合

设计相关 1. mastrtgo&#xff08;暂时免费&#xff09; &#xff1a;可以根据自然语言生成UI设计稿和前端代码 MasterGo 莫高设计 - AI 时代的数字界面生产平台 2. reddy.ai&#xff08;暂时免费&#xff09;: 国外类似mastrtgo的平台 Readdy 3. midjourney &#xff08;…...

CSDN 博客:CC++ 内存管理详解

CSDN 博客&#xff1a;C/C 内存管理详解 在软件开发过程中&#xff0c;内存管理是一个非常重要的环节。对于 C 和 C 这两种编程语言&#xff0c;它们都拥有独特的内存管理机制&#xff0c;理解这些机制对于编写高效、健壮的程序至关重要。本文将详细讲解 C/C 内存管理相关的内…...

表单制作代码,登录动画背景前端模板

炫酷动效登录页 引言 在网页设计中,按钮是用户交互的重要元素之一。一个炫酷的按钮特效不仅能提升用户体验,还能为网页增添独特的视觉吸引力。今天,我们将通过CSS来实现一个“表单制作代码,登录动画背景前端模板”。该素材呈现了数据符号排版显示出人形的动画效果,新颖有…...

嵌入式项目:STM32刷卡指纹智能门禁系统

本文详细介绍基于STM32的刷卡指纹智能门禁系统。 获取资料/指导答疑/技术交流/选题/帮助&#xff0c;请点链接&#xff1a; https://gitee.com/zengzhaorong/share_contact/blob/master/stm32.txt 1 系统功能 1.1 功能概述 本系统由STM32硬件端&#xff08;下位机&#xff09;…...

LeetCode 热题100 141. 环形链表

LeetCode 热题100 | 141. 环形链表 大家好&#xff0c;今天我们来解决一道经典的算法题——环形链表。这道题在 LeetCode 上被标记为简单难度&#xff0c;要求我们判断一个链表中是否存在环。下面我将详细讲解解题思路&#xff0c;并附上 Python 代码实现。 题目描述 给定一个…...

以绘图(绘制点、直线、圆、椭圆、多段线)为例子 通过设计模式中的命令模式实现

为了在命令模式的基础上实现撤销&#xff08;Undo&#xff09;和回退&#xff08;Redo&#xff09;功能&#xff0c;我们可以在每个命令类中记录一些必要的状态&#xff0c;允许我们撤销之前的操作&#xff0c;并在需要时回退操作。常见的做法是使用一个命令堆栈来存储历史命令…...

鹏哥c语言数组(初阶数组)

前言&#xff1a; 对应c语言视频54集 内容&#xff1a; 一维数组的创建 数组是一组相同元素的集合&#xff0c; 数组的创建方式 type_t就是数组的元素类型&#xff0c;const_n是一个常量表达式&#xff0c;用来指定数组的大小 c99标准之前的&#xff0c;数组的大小必须是…...

利用go-migrate实现MySQL和ClickHouse的数据库迁移

1. 背景 在使用gorm时 , 尽管已经有了自动建表和钩子函数 . 但是在面临希望了解到数据库的变更 , 和插入一些系统字段时 , 以及最关键的数据库迁移的工作 . gorm显得稍微有点不便 . 在了解到migrate这项技术后 , 就使用go-migrate开发了一个可以迁移MySQL和ClickHouse数据库的…...

计算机毕业设计SpringBoot+Vue.js企业客户管理系统(源码+LW文档+PPT+讲解+开题报告)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

jmeter 如何做移动端的测试 特别是兼容性测试

JMeter本身主要是一款用于性能测试和功能测试的工具,虽然它并非专门为移动端测试设计,但可以通过一些方式来对移动端应用进行测试,以下从测试准备、测试过程及注意事项等方面为你详细介绍: 一、测试准备 (一)环境搭建 JMeter安装与配置:确保JMeter已经正确安装在测试机…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...