反序列化漏洞1
一、PHP类与对象
1. 类
- 概念理解: 类是共享相同结构和行为的对象的集合,可以理解为特征的提取。例如将耳朵长、尾巴短、红眼睛、吃胡萝卜、蹦跳行走的动物特征抽象为"兔子"类。
- 代码结构:
- 使用class关键字定义类
- 类名遵循大驼峰命名法
- 包含成员变量(属性)和方法(行为)
- 成员组成:
- 属性: 描述对象的特征,如$var1、$var2
- 方法: 定义对象的行为,如myfunc()函数
- 抽象特性: 类是特征的抽象集合,不是具体实例
2. 对象
- 实例关系: 对象是类的具体实例,如"兔子类"的具体实例可以是"小白兔"
- 创建方式: 使用new关键字实例化对象
- 实际案例:
- $baidu = new Site; 创建网站类的百度实例
- $kitty = new Cat; 创建猫类的Kitty实例
- $benz = new Car; 创建汽车类的奔驰实例
- 面向对象原则: PHP遵循"一切皆对象"的面向对象编程思想
二、代码实例
- 类定义:
- 实例化过程:
- $obj = new ClassDemo(); 创建类实例
- $obj->echoString(); 调用对象方法
- 执行结果: 输出成员变量$var的字符串值
- 关键语法:
- public 访问修饰符
- $this 指代当前对象实例
- -> 对象操作符
三、知识小结
知识点 | 核心内容 | 考试重点/易混淆点 | 难度系数 |
PHP类与对象 | 类为共享结构和行为的对象集合(如Rabbit类抽象兔子特征);对象是类的实例(如new创建Baidu网站实例) | 类(抽象)vs对象(具体实例) | ⭐⭐ |
PHP序列化与反序列化 | 序列化函数使用、数据格式转换意义 | serialize()与unserialize()的安全风险 | ⭐⭐⭐ |
反序列化漏洞原理 | 恶意数据触发对象注入,破坏程序逻辑 | 魔术方法(如__wakeup())的利用 | ⭐⭐⭐⭐ |
CTF题目实战分析 | 外部网站漏洞挖掘与利用方法 | 输入点检测、POP链构造 | ⭐⭐⭐⭐ |
CMS漏洞案例(tCMS) | 实际漏洞复现与成因分析 | 未过滤用户输入导致对象注入 | ⭐⭐⭐⭐ |
漏洞修复与防御 | 输入验证、禁用危险函数、使用安全库 | __wakeup()方法的安全实现 | ⭐⭐⭐ |
一、PHP的magic函数
1. 函数作用
- 定义:魔术方法是以双下划线(__)开头的特殊方法,当对对象执行特定操作时会覆盖PHP的默认行为
- 数量:PHP中共有17个魔术方法,包括__construct()、__destruct()等
- 命名规范:PHP保留所有以__开头的方法名称,不建议自定义此类方法名
- 生命周期方法:
- __construct():对象创建时调用(通过new或反序列化)
- __destruct():对象销毁时调用(如脚本执行结束)
- 类型转换方法:
- __toString():对象被当作字符串使用时调用(如echo输出或字符串拼接)
- 序列化相关:
- __sleep():对象被序列化前调用
- __wakeup():对象被反序列化后调用
- __serialize():PHP7.4新增,替代__sleep()
- __unserialize():PHP7.4新增,替代__wakeup()
- 访问控制方法:
- __call():调用不可访问方法时触发
- __callStatic():静态调用不可访问方法时触发
- __get():读取不可访问属性时触发
- __set():写入不可访问属性时触发
- __isset():对不可访问属性调用isset()或empty()时触发
- __unset():对不可访问属性使用unset()时触发
- 其他方法:
- __invoke():将对象作为函数调用时触发
- __clone():对象克隆时调用
- __debugInfo():var_dump()对象时调用
- 访问限制:除__construct()、__destruct()和__clone()外,所有魔术方法必须声明为public
- 类型声明:PHP8.0起,魔术方法的类型声明必须与文档描述一致
2. 例题:magic函数案例使用
- 实现原理:
- 自动触发:魔术方法无需显式调用,满足条件时自动执行
- 生命周期控制:通过预定义方法实现对对象生命周期的统一管理
- 代码复用:避免在每个对象中重复编写相同逻辑
- 设计目的:
- 统一控制点:提供对象生命周期关键节点的标准控制接口
- 减少重复代码:集中处理对象创建、使用和销毁的通用逻辑
- 扩展性:允许开发者在不修改核心代码的情况下扩展对象行为
- 使用场景:
- 资源管理:在__construct中初始化资源,在__destruct中释放资源
- 调试跟踪:通过魔术方法记录对象操作日志
- 动态属性:使用__get/__set实现动态属性访问
- 方法重载:通过__call实现方法重载功能
- 注意事项:
- 性能影响:魔术方法调用会增加额外开销
- 命名冲突:避免自定义__开头的方法名
- 版本兼容:注意不同PHP版本对魔术方法的支持差异
二、知识小结
知识点 | 核心内容 | 关键特性/易混淆点 | 应用场景 |
__construct() | 对象创建时自动调用 | 通过new或反序列化触发 | 对象初始化、资源分配 |
__destruct() | 对象销毁时自动调用 | 程序结束或对象显式销毁时触发 | 资源释放、清理操作 |
__toString() | 对象被当作字符串使用时调用 | echo输出或字符串拼接时触发 | 对象字符串化表示 |
__sleep() | 对象序列化前执行 | 与serialize()相关 | 选择性序列化属性 |
__wakeup() | 对象反序列化后执行 | 与unserialize()相关 | 反序列化后初始化 |
__call() | 访问不可访问方法时触发 | 包含方法名和参数数组 | 动态方法处理 |
__callStatic() | 静态上下文访问不可访问方法时触发 | 静态方法调用拦截 | 静态方法重载 |
__get() | 读取不可访问属性时触发 | 属性访问拦截 | 动态属性处理 |
__set() | 写入不可访问属性时触发 | 属性赋值拦截 | 属性访问控制 |
__isset() | 对不可访问属性调用isset()或empty()时触发 | 属性存在性检查拦截 | 动态属性验证 |
__unset() | 对不可访问属性调用unset()时触发 | 属性删除拦截 | 属性删除控制 |
__invoke() | 对象被当作函数调用时触发 | 使对象可调用 | 函数式编程接口 |
__clone() | 对象克隆时触发 | 通过clone关键字调用 | 深拷贝控制 |
魔术方法特点 | 自动触发机制 | 以双下划线__开头 | 对象生命周期控制点 |
设计目的 | 统一对象行为控制 | 减少重复代码 | 提供预置控制点 |
一、PHP序列化和反序列化
1. 序列化
1)序列化与反序列化概述
- 序列化: 将对象转换成一个可存储的字符串,便于存储或传递PHP的值,同时不丢失其类型和结构。
- 反序列化: 将已序列化的字符串变回PHP的值。
- 魔术方法:
- _serialize(): 在序列化之前执行,返回一个代表对象序列化形式的关联数组。
- _unserialize(): 在反序列化时执行,用于恢复对象的属性。
- 注意:
- 如果类中同时定义了_serialize()和_sleep(),则只有_serialize()会被调用。
- 如果对象实现了Serializable接口,则接口的serialize()方法会被忽略,类中的serialize()方法会被调用。
2)serialize() 函数
- 功能: 产生一个可存储的值的表示,返回一个包含表示value的字节流的字符串。
- 处理类型: 可处理除了resource之外的任何类型,包括包含指向其自身引用的数组。
- 对象序列化:
- 在序列化对象时,PHP会尝试调用对象的_sleep()方法(如果存在_serialize()则忽略_sleep()),允许对象在被序列化之前做清除操作。
- 示例:
number=32;number = 32; number=32;
str = 'wuya';
bool=true;bool = true; bool=true;
null = NULL;
arr=array(′aa′=>1,′bbbb′=>9);arr = array('aa' => 1, 'bbbb' => 9); arr=array(′aa′=>1,′bbbb′=>9);
obj = new SerialType(data: 'somestr', pass: true);
var_dump(serialize(
number));//string(5)"i:32;"vardump(serialize(number)); // string(5) "i:32;" var_dump(serialize(number));//string(5)"i:32;"vardump(serialize(
str)); // string(11) "s:4:"wuya";"
var_dump(serialize(
bool));//string(4)"b:1;"vardump(serialize(bool)); // string(4) "b:1;" var_dump(serialize(bool));//string(4)"b:1;"vardump(serialize(
null)); // string(2) "N;"
var_dump(serialize(
arr));//string(34)"a:2:s:2:"aa";i:1;s:4:"bbbb";i:9;"vardump(serialize(arr)); // string(34) "a:2:{s:2:"aa";i:1;s:4:"bbbb";i:9;}" var_dump(serialize(arr));//string(34)"a:2:s:2:"aa";i:1;s:4:"bbbb";i:9;"vardump(serialize(
obj)); // string(75) "O:10:"SerialType":2:{s:4:"data";s:7:"somestr";s:16:"SerialTypepass";b:1;}"
###### 3)序列化结果解析 - **整数**: 以`i:`开头,后跟整数值,如`i:32;`。 - **字符串**: 以`s:`开头,后跟字符串长度和字符串内容,如`s:4:"wuya";`。 - **布尔值**: `b:1;`表示true,`b:0;`表示false。 - **空值**: 用`N;`表示。 - **数组**: 以`a:`开头,后跟元素个数和元素内容,如`a:2:{s:2:"aa";i:1;s:4:"bbbb";i:9;}`。 - **对象**: 以`O:`开头,后跟类名长度、类名、元素个数和元素内容,如`O:10:"SerialType":2: - **注意**: - 对象序列化时,私有成员变量名会拼接类名,如`s:16:"SerialTypepass";`。 - 常量不会被序列化。 ###### 4)反序列化 - **功能**: 将序列化的字符串恢复成PHP的值。 - **魔术方法**: - `_unserialize()`: 在反序列化时执行,用于恢复对象的属性。 - 如果类中同时定义了`_unserialize()`和`_wakeup()`,则只有`_unserialize()`会生效。###### 5)序列化与反序列化的应用 - **存储**: 可以将序列化后的字符串存储在文件、数据库、Redis等存储系统中。 - **传输**: 便于在网络中传输复杂的数据结构。##### 2. 其他序列化格式 <timestamp>574000</timestamp> ###### 1)序列化格式种类 - **json字符串**: 使用$json_encode$方法,常用于数据传输,如JAVA框架和消息中间件。 - **xml字符串**: 使用$wddx_serialize_value$方法,早年流行于web service数据交换。 - **二进制格式字节数组**: 也是一种序列化方式。 ###### 2)序列化示例 - **示例对象**: `JsonClass`类,包含字符串成员变量`word`和数组成员变量`prop`。 - **json序列化**: 使用`json_encode`方法,输出为json字符串。 - **xml序列化**: 使用`wddx_serialize_value`方法,输出为xml字符串。###### 3)序列化输出 - **json输出**: `{"word": "hello wuya", "prop": {"name": "wuya", "age": 31, "motto": "Apple keep doctor"}}` - **xml输出**: 包含`wddxPacket`、`header`、`data`、`struct`等标签,详细描述了对象的结构和内容。###### 4)XML格式化 - **格式化工具**: 可使用在线XML格式化工具查看XML结构,如[BEJSON] ###### 5)序列化与反序列化 - **反序列化**: 使用相应的方法(如`json_decode`、`wddx_deserialize`)可将序列化的字符串还原为对象。###### 6)敏感字段处理 - **敏感字段**: 如密码等,不需要序列化。 - **处理方法**: 重写`_sleep`方法,返回一个数组,包含需要序列化的变量名,排除敏感字段。###### 7)_sleep方法示例 - **示例代码**: `User`类重写`_sleep`方法,排除`password`字段。 - **序列化结果**: 序列化字符串中不包含`password`字段。###### 8)序列化应用 - **应用场景**: 将对象保存为字符串,存储在文件、数据库或通过网络传输,再在需要的地方还原使用。##### 3. 反序列化 <timestamp>851000</timestamp> ###### 1)反序列化例子 <timestamp>891000</timestamp>- **反序列化函数**: 使用 `unserialize()` 函数可以将已序列化的字符串还原成 PHP 的值或对象。 - **示例代码**: ```php class User {public$username;public$nickname;private$password;public function __construct($username,$nickname,$password) { $this->username =$username; $this->nickname =$nickname; $this->password =$password;} } // 序列化对象 $user = new User("hackerwuya", "wuya", "password123"); $serializedUser = serialize($user); echo$serializedUser; // 反序列化对象 $unserializedUser = unserialize($serializedUser); var_dump($unserializedUser);
- 注意事项:
- 如果反序列化的字符串格式不正确,unserialize() 将返回 FALSE。
- 如果反序列化的对象类不存在,将得到一个 __PHP_Incomplete_Class 对象,并且无法调用未定义类的方法。
- Magic 方法:
- _sleep(): 在对象序列化之前调用,可以用于清理对象。
- _wakeup(): 在对象反序列化之后调用,可以用于重新初始化对象。
- 反序列化后的对象方法调用: 反序列化后的对象可以调用其定义的方法,如 echoString()。
- 篡改序列化字符串: 反序列化过程中可以篡改序列化字符串中的值,但需注意保持格式正确。
- 类不存在时的处理:
- 如果反序列化的类不存在,PHP 将返回一个 __PHP_Incomplete_Class 对象。
- 尝试调用未定义类的方法将导致致命错误。
- 反序列化注意事项:
- 如果传递的字符串不可序列化,unserialize() 返回 FALSE。
- 如果对象类未定义,反序列化得到的对象是 __PHP_Incomplete_Class。
- 格式错误处理: 如果序列化字符串格式错误,unserialize() 将无法正确解析并返回 FALSE。
4. 序列化和反序列化的作用
1)序列化和反序列化的概念
- 序列化: 对象和字符串之间的转换过程,将对象转换为字符串,以便进行传输或存储。
- 反序列化: 将序列化后的字符串转换回对象的过程。
- 注意:
- 如果传递的字符串不可以序列化,则返回FALSE。
- 如果对象没有预定义,反序列化得到的对象是
PHP_Incomplete_ClassPHP\_Incomplete\_ClassPHP_Incomplete_Class
。
2)序列化和反序列化的作用
- 传输对象: 序列化可以将对象转换为字符串,方便在网络中传输或存储到文件中,然后在需要的地方进行反序列化,恢复成对象。
- 用作缓存: 序列化后的对象可以存储到Cookie或Session中,作为缓存数据。在需要时,通过反序列化可以快速恢复对象状态。
- 配合magic方法: 在反序列化时,可以自动触发magic方法(如__wakeup()),进行一些初始化或清理工作。
3)序列化和反序列化与magic函数的关联
- magic函数: 在PHP中,magic函数是指一些以双下划线(__)开头的方法,它们在特定情况下会自动被调用。
- 关联: 反序列化过程中,如果对象类中定义了__wakeup()等magic方法,这些方法会在反序列化时自动被调用,可以用于执行一些初始化操作或恢复对象状态。
5. 反序列化与Magic函数
1)序列化和反序列化例题
- _unserialize()与_wakeup()的关系
- 关系: 如果类中同时定义了_unserialize()和_wakeup()两个魔术方法,则只有_unserialize()方法会生效,_wakeup()方法会被忽略。
- 版本: 此特性自PHP7.4.0起可用。
- _serialize()与_sleep()的关系
- 关系: 如果类中同时定义了_serialize()和_sleep()两个魔术方法,则只有_serialize()方法会被调用,_sleep()方法会被忽略。
- 接口: 如果对象实现了Serializable接口,接口的serialize()方法会被忽略,作为代替类中的_serialize()方法会被调用。
- _unserialize()的用途
- 用途: _unserialize()用于定义对象序列化友好的任意表示,数组的元素可能对应对象的属性,但这并不是必须的。
- 触发: 在反序列化时,如果存在_unserialize()魔术方法,则会自动调用此方法,并传递从_serialize()返回的恢复数组。
- 示例代码
- 代码示例:
this−>dsn=this->dsn =this−>dsn=
dsn;
this−>username=this->username =this−>username=
username;
this−>password=this->password =this−>password=
password;
this->connect(); } public function __serialize() { echo "_serialize\n"; } public function __unserialize(
data) {
echo "_unserialize\n";
}
public function __wakeup() {
echo "_wakeup\n";
}
}
- **测试**: 通过创建对象并调用unserialize()方法,观察_serialize()、_unserialize()和_wakeup()的触发情况。 ###### 2)版本影响与测试 - 版本差异 - **版本差异**: _unserialize()方法自PHP7.4.0起可用,在低于此版本的PHP中,反序列化时会调用_wakeup()方法。- **测试方法**: 通过在不同PHP版本下运行相同的测试代码,观察_unserialize()和_wakeup()的调用情况。 - 测试示例 - **测试代码**: ```php $obj1 = new UnSerializeTest(); $obj2 = unserialize('0:15:"UnSerializeTest":1:{s:3:"var";s:15:"hello wuyaziabc";}'); $obj2->echoString();
- 结果分析: 在PHP7.4.0及以上版本,反序列化时会调用_unserialize()方法;在低于此版本的PHP中,会调用_wakeup()方法。
3)PHP版本管理
- PHP Study工具
- 工具介绍: PHP Study是一个本地的集成工具,可以方便地下载和安装不同版本的PHP。
- 版本管理: 通过在PHP Study中添加不同版本的PHP,可以方便地在不同项目中使用不同版本的PHP进行开发和调试。
- 版本配置
- 配置方法: 在项目的配置中指定使用的PHP版本路径,即可使用该版本的PHP进行运行和调试。
- 注意事项: 确保指定的PHP版本路径正确,且该版本的PHP已正确安装和配置。
二、知识小结
知识点 | 核心内容 | 考试重点/易混淆点 | 难度系数 |
PHP序列化和反序列化 | PHP对象存活于内存时可访问属性和方法,消亡后则不可访问;序列化是将对象转换成字符串形式以便存储和传输,反序列化是将字符串恢复成对象。 | 序列化和反序列化的概念、方法、应用场景。 | ★★★ |
序列化方法 | 使用serialize()方法将对象转换成字符串,可存储于文件、数据库、Redis等。 | serialize()方法的使用,不同数据类型序列化后的格式。 | ★★★★ |
反序列化方法 | 使用unserialize()方法将字符串恢复成对象。 | unserialize()方法的使用,反序列化后的对象状态。 | ★★★ |
序列化格式解读 | 整数、字符串、布尔值、空值、数组、对象的序列化格式。 | 各种数据类型序列化后的字符串格式,特别是对象和数组的复杂格式。 | ★★★★☆ |
序列化注意事项 | 对象序列化时不包括方法和常量,只包括类名、属性和值。 | 方法和常量不被序列化,私有成员变量序列化时包含类名。 | ★★★ |
其他序列化格式 | JSON、XML、二进制等序列化格式。 | JSON和XML序列化的方法和应用场景。 | ★★ |
防止敏感信息序列化 | 重写sleep方法,返回需要序列化的属性数组,排除敏感信息。 | sleep方法的重写,防止敏感信息被序列化。 | ★★★★ |
反序列化注意事项 | 反序列化时类不存在会报错,但仍可返回对象;字符串格式错误会返回false。 | 类不存在时的反序列化结果,字符串格式错误的处理。 | ★★★★ |