java反序列化
Java 反序列化基础:gadget 链 & ysoserial
题目
- 什么是 gadget 链?为什么 Java 反序列化漏洞必须要有 gadget 链才能 RCE?
- ysoserial 工具的作用是什么?它为什么需要
CommonsCollections3、CommonsBeanutils1这样的依赖版本? - 为什么同一个 Shiro-550 payload,打 A 系统能 RCE,打 B 系统却只能反序列化但执行不了命令?
参考答案
1. 什么是 gadget 链
一句话定义:gadget 链是一组现有类中可被"串起来"调用的方法,最终能达到攻击者想要的效果(通常是执行命令)。
为什么必须要有 gadget 链?
Java 原生反序列化(ObjectInputStream.readObject())本身并不直接执行命令,它只是把字节流还原成对象。漏洞能 RCE 的关键在于:
恶意序列化字节流
→ readObject() 触发
→ 自动调用对象的 readObject / readResolve / 等魔术方法
→ 这些方法又调用了其他方法
→ ... 像多米诺骨牌一样链式调用 ...
→ 最终调到 Runtime.exec() 或 ProcessBuilder.start()
→ 执行命令
这一串"从入口到 RCE 终点"的方法调用链就是 gadget 链。
关键认知:
- gadget 链不是攻击者"注入"进去的代码
- 它是目标系统 classpath 上已经存在的代码
- 攻击者只是通过精心构造的序列化数据,触发这些现成类的方法按特定顺序被调用
- 所以 Java 反序列化漏洞的利用,本质上是一种"代码复用攻击"(类似二进制领域的 ROP)
2. ysoserial 工具的作用
ysoserial 是业界最出名的 Java 反序列化 payload 生成工具,它的核心作用是:
攻击者输入:想执行的命令(如 "calc.exe")
ysoserial 输出:一段二进制序列化字节流
这段字节流被目标反序列化时,
会触发特定 gadget 链,最终执行命令
为什么需要指定依赖版本(如 CommonsCollections3)?
因为每条 gadget 链都依赖特定库的特定版本的内部实现:
| Gadget 链 | 依赖库 | 备注 |
|---|---|---|
| CommonsCollections1 | commons-collections 3.1 | 经典老链,高版本 JDK 失效 |
| CommonsCollections3 | commons-collections 3.1 | 绕过部分 JDK 限制 |
| CommonsCollections5/6 | commons-collections 3.1-3.2.1 | 主流稳定链 |
| CommonsBeanutils1 | commons-beanutils 1.9.2 | Shiro 自带依赖 |
| ROME | rome 1.0 | 某些特殊场景 |
| Jdk7u21 | JDK ≤ 7u21 | 无需外部依赖 |
ysoserial 的本质:它是一个 gadget 链的"字典库",根据你选的链和命令,拼装出对应的序列化字节流。
示例命令:
java -jar ysoserial.jar CommonsCollections5 "calc.exe" > payload.bin
3. 为什么"同一个 payload,A 能打 B 打不了"
这是面试官最喜欢问的深度追问题,核心答案是:gadget 链依赖的库,目标系统必须存在。
A 系统能打的条件:
✅ Shiro 版本 < 1.2.4(默认 key 存在)
✅ classpath 上有 commons-collections 3.2.1
✅ JDK 版本兼容该 gadget 链
✅ 未部署 RASP / 反序列化黑名单
B 系统打不了的常见原因:
| 现象 | 原因 |
|---|---|
| 能解密、能反序列化,但没命令执行 | classpath 上没有 gadget 链依赖(如缺 commons-collections) |
| 解密后抛 ClassNotFoundException | gadget 链中某个类目标系统找不到 |
| 反序列化抛 InvalidClassException | 类存在但 serialVersionUID 不匹配 |
| 完全无响应 | WAF 拦截 / 服务端换了非默认 key |
| 本地测试成功,打线上失败 | JDK 版本不一致(如目标是 JDK 17,部分老链失效) |
排查思路(实战面试会问到):
- 先用 CommonsBeanutils1 链探测(Shiro 自带依赖,命中率高)
- 再依次尝试 CC3、CC5、CC6
- 使用 DNSLog 探测链是否触发(
URLDNS链只验证反序列化、不执行命令) - 若全部无响应,考虑目标做了反序列化过滤,需要找绕过链或不出网场景的内存马
4. 一张图串联 Shiro-550 + gadget + ysoserial
攻击端:
[命令 "id"]
→ ysoserial 用 CB1 链生成 [恶意序列化字节流]
→ AES 加密(用已知默认密钥)
→ Base64 编码
→ 塞入 Cookie: rememberMe=xxx
服务端:
收到请求 → Base64 解码 → AES 解密
→ ObjectInputStream.readObject() 【反序列化入口】
→ 自动触发 CB1 链的魔术方法
→ 一路调到 Runtime.exec("id") 【命令执行】
→ 返回结果
关键一句话总结:Shiro-550 的"漏洞点"是默认密钥 + 无过滤反序列化,而"RCE 能力"来自 classpath 上现成的 gadget 链。两者缺一不可。
本文来自博客园,作者:Doll_Marker,转载请注明原文链接:https://www.cnblogs.com/dollaikun/p/20633921

浙公网安备 33010602011771号