php框架反序列化漏洞审计需定位unserialize危险调用点、构造pop链验证rce,再通过allowed_classes白名单、hmac-sha256签名校验及禁用phar等措施实现防御闭环。

在PHP框架审计中定位反序列化漏洞并完成利用与防御闭环,必须从代码层识别危险调用点、构造可控POP链、验证RCE效果,再落地到白名单限制与签名校验等硬性防护措施。
定位框架中不安全的unserialize调用点
打开框架源码全局搜索unserialize(,重点关注请求入口、Session处理、缓存加载、日志回放等模块;特别留意参数来自$_GET、$_POST、$_COOKIE、$_REQUEST或文件读取的场景。
检查调用是否直接包裹用户输入:如unserialize($_GET['data'])、unserialize(base64_decode($_COOKIE['session']))、unserialize(file_get_contents($cache_file))——只要数据源头不可信,就构成高危路径。
【必须确认unserialize前无任何过滤或来源校验】若发现调用前有is_numeric()、filter_var()等仅校验基础类型的操作,仍无法阻止对象反序列化,该点仍可利用。
验证是否存在可用POP链与Gadget类
方法一:静态分析框架vendor目录下的第三方库,用PHPGGC或GadgetInspector扫描已知POP链;重点关注monolog、swiftmailer、symfony/serializer、laravel/framework等组件中含__destruct、__wakeup且调用system、file_put_contents、eval、call_user_func的类。
方法二:动态触发测试——构造最简payload:O:8:"stdClass":0:{}传入目标点,观察是否报错Class stdClass not found或Object of class stdClass could not be converted to string,前者说明反序列化执行成功但类不存在,后者说明__toString被触发,存在利用可能。
方法三:使用php -r "echo urlencode(serialize(['a'=>new SplFileObject('/etc/passwd')]));"生成含基础Gadget的序列化字符串,发送后观察响应是否包含passwd内容——若出现,则证明POP链可触发文件读取。
构造并触发RCE型POP链
第一步:确认目标框架使用的PHP版本 ≥ 7.0,确保allowed_classes参数可用;低于7.0则跳过白名单加固环节,专注禁用PHAR和签名校验。
第二步:从框架已加载类中筛选具备RCE能力的Gadget,例如Laravel 8.75以下版本中的Illuminate\Broadcasting\PendingBroadcast类,其__destruct()会调用$this->events->dispatch(),配合Illuminate\Events\Dispatcher的__call()可触发任意方法。
第三步:用PHPGGC生成对应框架版本的payload:./phpggc monolog/rce1 'phpinfo();' --fast-return | base64 -w0,将base64结果URL编码后注入目标参数。
第四步:发送请求,观察HTTP响应体是否包含phpinfo()输出或系统命令执行痕迹;若返回空白但服务器端产生异常进程(如sh -c),说明RCE已触发但输出未回显,需改用DNSLog或写WebShell验证。
部署allowed_classes白名单防御
找到所有unserialize()调用位置,在第三个参数传入精确类名数组:unserialize($data, ['allowed_classes' => ['App\Models\User', 'App\Services\CacheItem']]);
注意命名空间必须完整匹配,User和App\Models\User视为不同类;若某处需反序列化任意基础类型(如数组、字符串),则设为false:unserialize($data, ['allowed_classes' => false]);
【禁止设置allowed_classes为true或省略该参数】PHP默认行为即允许全部类反序列化,等同于未设防。
增加HMAC-SHA256签名校验
服务端序列化后立即计算签名:$signed = $serialized . '|' . hash_hmac('sha256', $serialized, $_ENV['SIGNING_KEY']);
反序列化前强制校验:[$raw, $sig] = explode('|', $signed, 2); if (!hash_equals(hash_hmac('sha256', $raw, $_ENV['SIGNING_KEY']), $sig)) { die('Signature mismatch'); }
密钥严禁硬编码,必须从环境变量或配置中心加载;若使用md5或sha1,攻击者可利用长度扩展攻击伪造签名,【必须使用HMAC-SHA256及以上强度哈希】。
关闭PHAR协议与加固Session配置
编辑php.ini,添加两行:phar.readonly = On和session.serialize_handler = php_serialize;重启PHP-FPM使生效。
检查当前session.serialize_handler值是否为php或igbinary,若是则存在处理器差异导致的反序列化绕过风险,必须切换至php_serialize。
验证PHAR禁用效果:尝试访问http://target.com/test.phar,应返回404或“File not found”,而非触发Phar::webPhar()自动反序列化。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











