针对 Cotonti 的 CommentsWidget 实现中发现的 PHP 对象注入问题的安全本地概念验证。
plugins/comments/inc/CommentsWidget.php存在漏洞的代码路径接受 ci GET 参数,并将攻击者控制的数据通过 base64_decode() 传入 PHP 的 unserialize(),且未限制可实例化的类。
从概念上讲,存在漏洞的操作是:
$ci = @unserialize(base64_decode($ci));
当提供序列化对象时,PHP 可能会实例化攻击者控制的类,并调用诸如 __wakeup() 之类的魔术方法。在更大的应用程序中,当存在合适的 gadget 类时,这可能变得危险。
提交的修复将反序列化改为:
$ci = @unserialize(base64_decode($ci), ['allowed_classes' => false]);
并在使用结果值之前验证其是否具有预期的数组结构。
poc.php 执行三项本地测试:
unserialize() 实例化 DangerousGadget 并触发其 __wakeup() 方法。allowed_classes => false 阻止 gadget 类被实例化,因此阻止了 __wakeup() 副作用。该 PoC 仅使用 /tmp/pwned_by_cotonti_poc.txt 作为无害的哨兵文件,表明 __wakeup() 已执行。它不会执行操作系统命令。id && whoami 字符串仅作为 gadget 的 command 属性中的占位值存在。
serialize() / unserialize() 的 PHP CLI无需安装 Cotonti,因为存在漏洞和修复后的反序列化行为是直接复现的。
在仓库目录中执行:
php poc.php
预期输出类似于:
[!] DangerousGadget::__wakeup() triggered
[TEST 1] VULNERABLE: __wakeup() executed during unrestricted unserialize().
[TEST 2] FIXED: object instantiation was blocked; no sentinel was created.
[TEST 3] FIXED: invalid serialized structure rejected by validation.
Summary:
- Vulnerable path allows a serialized attacker-controlled object to be instantiated and invokes __wakeup().
- Fixed path uses allowed_classes=false, preventing the gadget class from being instantiated.
- Structural validation rejects values that do not match the expected array shape.
- No real command execution is performed by this PoC; the command property is only a placeholder.
脚本在退出前会删除其哨兵文件。
不要使用不受限制的 PHP unserialize() 反序列化攻击者控制的数据。
对于此代码路径,所演示的缓解措施是禁用类实例化:
unserialize($data, ['allowed_classes' => false]);
在使用反序列化值的内容之前,还应按照应用程序期望的确切结构对其进行验证。
本仓库旨在用于安全研究、验证和防御性测试。仅在你拥有或明确获授权测试的环境中运行该 PoC。未经许可,请勿将其用于针对系统。
Harsh Raj Singhania — 漏洞研究、概念验证和披露。