当前位置:首页>php>Day77 PHP反序列化&字符逃逸&增多减少&成员变量属性&解析不敏感&Wakeup绕过

Day77 PHP反序列化&字符逃逸&增多减少&成员变量属性&解析不敏感&Wakeup绕过

  • 2026-10-11 07:21:32
Day77 PHP反序列化&字符逃逸&增多减少&成员变量属性&解析不敏感&Wakeup绕过

一、PHP反序列化基础原理

1.1 序列化与反序列化概念

序列化(serialize()):将PHP对象、数组等复杂数据类型,转换为可存储、可传输的字符串格式,本质是对数据结构的格式化编码。

反序列化(unserialize()):将序列化后的字符串,还原为原始的PHP对象或数组。核心风险:用户可控反序列化字符串时,可通过构造恶意字符串,触发类中魔术方法,实现代码执行、文件操作等恶意行为。

1.2 基础序列化格式规则

对象格式:O:类名长度:"类名":成员变量数量:{变量序列化内容}

私有变量:序列化后会携带类名填充,格式为 \0类名\0变量名(不可见空字符 \0)

保护变量:序列化后携带 \0*\0变量名 填充

公有变量:直接序列化变量名,无额外填充字符

1.3 核心魔术方法

反序列化过程中,无需手动调用,满足条件自动触发,是漏洞利用核心:

__wakeup():反序列化完成瞬间优先触发(优先级最高)

__construct():类实例化时触发,反序列化默认不触发

__destruct():对象销毁时触发(脚本结束、对象被回收)

__toString():对象被当作字符串使用时触发

__call():调用对象不存在方法时触发

二、成员变量属性

PHP类中三种变量修饰符,序列化后的格式、长度、解析规则完全不同,是字符逃逸、变量数量绕过的基础前提。

2.1 三种属性变量序列化细节

变量属性

修饰符

序列化格式

实际长度变化

公有变量

public

直接变量名,如 s:1:"a"

无额外字符,长度=变量名长度

保护变量

protected

\0*\0变量名

固定多2个空字符,长度+2

私有变量

private

\0类名\0变量名

空字符+类名长度,长度大幅增加

2.2 核心作用

在字符逃逸场景中,私有/保护变量的隐藏填充字符 会改变序列化字符串总长度,配合过滤函数的字符替换、删除逻辑,可实现「字符逃逸」,篡改原有序列化结构。

三、PHP反序列化解析不敏感特性

PHP unserialize() 函数解析字符串时,存在容错不敏感特性,不会严格校验格式完整性,是多种绕过漏洞的核心依据。

3.1 类名大小写不敏感

反序列化解析类名时,忽略大小写。例如类名 Test,序列化字符串中写 test / TEST 均可正常解析,成功实例化对象。

利用场景:WAF拦截特定类名、黑名单过滤大小写敏感字符时,可大小写混淆绕过。

3.2 多余字符自动忽略

unserialize() 只会解析合法序列化结构部分,字符串末尾多余的乱码、无效字符、恶意拼接内容会被直接忽略,不影响反序列化执行。

3.3 变量数量解析不严格

序列化字符串中声明的「成员变量数量」,与实际写入的变量数量不强制匹配:

声明数量 > 实际变量数:正常解析,缺失变量默认赋值为空

声明数量 < 实际变量数:正常解析,多余变量自动忽略

四、字符逃逸(增多逃逸 & 减少逃逸)

字符逃逸核心场景:代码存在过滤/转义函数(如 addslashes、自定义字符替换、黑名单删除),用户输入可控,过滤函数会改变字符数量,导致原有序列化结构错位、逃逸,最终篡改变量值。

核心原理:输入字符经过过滤后,长度发生变化,打破原有序列化字符串的长度匹配规则,实现恶意变量注入。

4.1 减少逃逸(过滤删除字符,长度缩短)

原理

系统过滤函数会删除/替换用户输入的特定字符,导致最终拼接的序列化字符串长度比预期更短,后方合法序列化结构向前错位,恶意构造的变量声明逃逸生效。

场景复现逻辑

1用户输入可控参数,拼接至序列化字符串中;

1后端过滤函数删除输入中的特定字符(如 \、空格、特定字母);

1字符被删除后,预设的长度数值失效,后方正常结构被篡改;

1成功注入恶意变量,覆盖原有变量值。

核心特点

过滤后字符总数 < 原始字符数,吃掉占位长度,实现结构逃逸。常见于 str_replace 删除字符场景。

4.2 增多逃逸(过滤增加字符,长度变长)

原理

系统过滤函数会转义特殊字符(如 addslashes 将 '"\ 转义为 \'\"\\),单个字符变为两个字符,导致序列化字符串长度增加。

场景复现逻辑

1用户输入携带特殊字符,后端自动转义,字符数量增多;

1序列化字符串中「声明的字符长度」是转义前的数值,小于实际转义后长度;

1长度不匹配导致原有闭合结构失效,恶意拼接的序列化代码逃逸执行。

经典案例:addslashes 字符逃逸

用户输入 %00 空字符或 \,被转义为 \\,长度+1,持续拼接后可突破原有长度限制,注入恶意变量。

4.3 增减逃逸核心区别

减少逃逸:过滤删字符 → 总长度变小 → 结构前移 → 篡改后端变量

增多逃逸:过滤加字符 → 总长度变大 → 闭合失效 → 恶意代码溢出

五、变量增多/减少绕过

基于前文解析不敏感(变量数量不严格校验)特性衍生的绕过手法,核心是篡改序列化字符串中的变量数量声明值。

5.1 变量减少绕过

场景:代码校验变量数量不能过多,限制传入参数数量。

绕过思路:序列化字符串中减小变量声明数量,实际保留恶意变量。PHP解析时忽略数量不匹配,正常解析所有变量,绕过数量校验。

5.2 变量增多绕过

场景:代码限制变量数量过少,或固定校验变量个数。

绕过思路:序列化字符串中增大变量声明数量,超出原有正常变量数。PHP自动忽略多余声明,正常解析有效变量,规避校验规则。

总结核心:PHP反序列化不校验变量数量一致性,数量声明值可随意篡改,仅以实际存在的变量为准。

六、__wakeup() 绕过技术

6.1 Wakeup函数原生特性

__wakeup() 优先级最高,反序列化时最先执行,常被开发者用于防护:清空恶意变量、重置参数、过滤危险内容,阻止反序列化漏洞利用。

6.2 经典绕过:变量数量溢出绕过(CVE-2016-7124)

漏洞原理:PHP版本 < 5.6.25 / < 7.0.10 存在解析BUG,当序列化字符串中声明的成员变量数量 大于 类实际定义的成员变量数量时,__wakeup() 函数失效不执行,仅触发 __destruct()。

6.3 绕过步骤

1查看目标类中实际定义的成员变量个数(假设真实变量数为2);

1修改序列化字符串中变量数量字段,改为大于真实值(如改为99);

1反序列化时触发BUG,__wakeup() 跳过不执行;

1直接执行 __destruct() 等后置魔术方法,完成漏洞利用。

6.4 绕过示例对比

正常序列化:O:4:"Test":2:{s:1:"a";s:1:"1";s:1:"b";s:1:"2";}(2个变量,wakeup正常执行)

恶意绕过序列化:O:4:"Test":99:{s:1:"a";s:1:"1";s:1:"b";s:1:"2";}(声明99个变量,wakeup失效)

6.5 拓展:其他Wakeup绕过方式

类名大小写绕过:利用解析大小写不敏感,混淆类名,导致wakeup匹配失效;

字符逃逸绕过:通过字符逃逸篡改类结构,破坏wakeup触发条件;

版本特性绕过:高版本PHP可结合解析冗余字符、变量覆盖绕过wakeup防护逻辑。

最新文章

随机文章