java反序列化

Java 反序列化基础:gadget 链 & ysoserial

题目

  1. 什么是 gadget 链?为什么 Java 反序列化漏洞必须要有 gadget 链才能 RCE?
  2. ysoserial 工具的作用是什么?它为什么需要 CommonsCollections3CommonsBeanutils1 这样的依赖版本?
  3. 为什么同一个 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,部分老链失效)

排查思路(实战面试会问到):

  1. 先用 CommonsBeanutils1 链探测(Shiro 自带依赖,命中率高)
  2. 再依次尝试 CC3、CC5、CC6
  3. 使用 DNSLog 探测链是否触发(URLDNS 链只验证反序列化、不执行命令)
  4. 若全部无响应,考虑目标做了反序列化过滤,需要找绕过链不出网场景的内存马

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 链。两者缺一不可。

posted @ 2026-06-18 16:09  Doll_Marker  阅读(39)  评论(0)    收藏  举报