[网鼎杯 2020 青龙组]AreUSerialz(反序列化,弱比较)
题目分析
题目概述
本题是一道典型的 PHP 反序列化漏洞题目,主要考察对 PHP 反序列化机制和比较运算符的理解。
关键代码
1 |
|
漏洞分析
核心漏洞:比较运算符不一致
这是本题的关键漏洞!代码中存在比较运算符不一致的问题:
__destruct()使用严格比较:$this->op === "2"process()使用非严格比较:$this->op == "2"
漏洞原理
在 PHP 中:
- 严格比较
===:要求值和类型都相同 - 非严格比较
==:只要求值相同,会进行类型转换
| 比较表达式 | 结果 | 原因 |
|---|---|---|
2 === "2" |
false |
类型不同(整数 vs 字符串) |
2 == "2" |
true |
值相同(都会转换为数字2) |
利用思路
需要构造一个反序列化 payload,使得:
__destruct()中$this->op === "2"返回false(不修改 op 值)process()中$this->op == "2"返回true(执行 read 方法)
Payload 构造
方法一:使用整数类型的 op
1 | O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";} |
解释:
O:11:"FileHandler":反序列化为 FileHandler 对象s:2:"op";i:2:设置op为整数 2(不是字符串 “2”)s:8:"filename";s:8:"flag.php":设置要读取的文件为 flag.php
方法二:使用公共属性覆盖(无需前缀)
由于代码直接访问 $this->op,我们可以使用公共属性名称,不需要 %00*%00 前缀:
1 | // 类中定义的是 protected $op |
原理:当反序列化时创建公共属性 $op,PHP 会优先访问公共属性。
完整利用过程
1. 发送 Payload
1 | ?str=O:11:"FileHandler":2:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";} |
2. 执行流程
1 | 反序列化 → 对象创建 → __destruct() 调用 |
3. 获取 Flag
执行后并没有显示 flag 内容,说明文件没有直接输出flag,而是把flag设置在变量里
可以把payload中的filename改为php://filter/convert.base64-encode/resource=flag.php,获得文件的base64编码

然后解码得到flag

技术要点总结
1. PHP 反序列化属性访问优先级
| 属性类型 | 访问优先级 | 序列化格式 |
|---|---|---|
| 公共属性 | 最高 | 属性名 |
| protected | 次之 | \x00*\x00属性名 |
| private | 最低 | \x00类名\x00属性名 |
2. 比较运算符差异
1 | // 严格比较(类型+值) |
3. is_valid 函数绕过
1 | function is_valid($s) { |
该函数只检查可打印 ASCII 字符(32-125),我的 payload 完全由可打印字符组成,因此可以顺利通过检查。
Flag
1 | flag{feff0449-1519-4e96-a316-c09b469eb5d6} |
总结
本题的核心在于比较运算符不一致导致的漏洞。通过巧妙构造反序列化 payload,利用整数和字符串的弱比较特性,成功绕过了 __destruct() 的限制,最终读取了 flag 文件。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Hello World!
