写在前面:合法学习边界
本文复现只在本机靶场进行。Shiro 反序列化利用可直接控制目标机器,禁止对非授权目标使用。
一、先分清两个漏洞
Shiro 的 rememberMe 反序列化问题常被笼统叫“Shiro 反序列化”,但实战中要严格区分两个编号,利用方式完全不同:
1
2
| Shiro-550(CVE-2016-4437):密钥泄露型
Shiro-721:Padding Oracle 型(不依赖密钥)
|
1
2
| 550:rememberMe 用硬编码默认密钥 AES 加密 → 知道密钥就能构造
721:密钥是随机的 → 用 CBC Padding Oracle 逐字节改密文,不猜密钥
|
识别第一步永远先看密钥:能解出默认密钥就按 550 打;解不出再考虑 721。
二、Shiro-550:硬编码密钥反序列化
原理
1
2
3
4
| 1. rememberMe Cookie = AES(序列化对象)
2. 默认密钥 kPH+bIxk5D2deZiIxcaaaA== 是公开的
3. 攻击者用同一把密钥加密恶意序列化对象
4. 服务端反序列化执行 → RCE
|
常见默认密钥:
1
2
| kPH+bIxk5D2deZiIxcaaaA==
4AvVhmFLUs0KTA3Kprsdag==
|
影响版本:Shiro <= 1.2.4,以及使用弱/默认密钥的版本。
影响
1
2
3
| 远程代码执行(RCE)
服务器被直接控制
内网横向
|
风险等级:严重。
利用
1
2
3
| 1. ysoserial 生成 gadget(CommonsCollections6 "id")
2. 用默认 AES 密钥加密
3. Base64 后放进 rememberMe Cookie
|
常用工具:ShiroExploit / Shiro_attack-2.2,内置默认密钥字典一键打。
检测与修复
1
2
| 检测:抓 rememberMe Cookie,用默认密钥字典解密
修复:升级 Shiro 1.4.2+,自定义高强度密钥,密钥不进代码库
|
三、Shiro-721:Padding Oracle 利用
原理
721 的密钥是随机生成的,但 rememberMe 用的是 AES-CBC 模式。CBC 的解密过程里,前一组的密文(或 IV)被拿来 XOR 后一组明文,攻击者可以通过不断修改密文、观察服务端是否报错(PaddingError/deleteMe 差异),逐字节恢复明文或构造合法密文。
影响版本:Shiro < 1.4.2(CBC 模式,且报错信息可区分)。
影响
1
2
| 同 550:远程代码执行
但需要大量请求(打一次可能几万次试错)
|
风险等级:高到严重(条件更苛刻)。
利用要点
1
2
3
| 1. 需要一个合法可解密的 rememberMe(先登录拿一个)
2. 基于 Padding Oracle 修改密文 → 最终恢复 AES 密钥或直接构造恶意明文
3. 用恢复出的密钥/构造结果打反序列化
|
工具:shiro_721_exp、部分 ShiroExploit 版本支持。
为什么难
1
2
| 耗时高、请求量大、容易触发 WAF 告警
不同版本对 PaddingError 的回显差异不同
|
检测与修复
1
2
| 检测:大量 deleteMe/异常长 rememberMe 请求、高频试错流量
修复:升级 Shiro 1.4.2+(改用 GCM 等认证加密模式),并升级反序列化防护
|
四、统一防御与修复
1
2
3
4
5
6
| 升级 Shiro 到 1.4.2+,关闭 CBC 弱模式
自定义高强度密钥并定期轮换
反序列化加类白名单
WAF 拦截异常长的 rememberMe
监控批量探测行为
修复后复测:550 默认密钥解不出、721 试错被阻断
|
五、小结
遇到 Shiro,先分 550 还是 721:先试默认密钥(550),解不出再考虑 Padding Oracle(721)。两者编号不同、原理不同、利用成本不同,混为一谈会在实战里浪费时间。