环境:

JDK 8u71:

https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html

Comoons-Collections 3.2.1

CC6 链分析:

在 CC1 中分析过 JDK 8u71之后的 AnnotationInvocationHandler.readObject 的写法改变,导致 CC1 链用不了,也就是说:

AnnotationInvocationHandler.readObject

AnnotationInvocationHandler.invoke.memberValues.get

这半条链子用不了,所以要找一个替代,这个替代也应该调用了 LazyMap.get 方法,之后的链子和 CC1 一样。

1、分析

我们找到 TiedMapEntry.getValue 中调用了 get 方法,

而 TiedMapEntry.hashCode 调用了 getValue ,

那么我们找哪里调用了 TiedMapEntry.hashCode 就可以完成利用链的跟踪

这里我用的是《Java 安全漫谈》给出的思路:

ysoserial 中,是利用 java.util.HashSet#readObject 到 HashMap#put() 到 HashMap#hash(key),最后到 TiedMapEntry#hashCode()。

实际上我发现,在 java.util.HashMap#readobject 中就可以找到 HashMap#hash() 的调用,去掉了最前面的两次调用:

在 HashMap 的 readObject 方法中,调用到了 hash(key),而 hash 方法中,调用到了 key·hashCode()。所以,我们只需要让这个 key 等于 TiedMapEntry 对象,即可连接上前面的分析过程,构成一个完整的 Gadget。

2、构造 POC

初步构造:

但是执行后无事发生,并没有弹出计算器。

下断点调试:

发现在 if (map.containsKey(key) == false) { key 的值为 “key”【也就是这一步 TiedMapEntry tme = new TiedMapEntry(outerMap, “key”); 】,并没有去执行 factory.transform(key) 。

在 TiedMapEntry tme = new TiedMapEntry(outerMap, “aaa”); 处下断点调试:

可以看到 key 来源于实例化时的传参

重新修改 POC:

3、总结

利用 LazyMap 在取值时会调用其 factory.transform(),再借助 TiedMapEntry 作为 HashMap 的 key,在反序列化时 HashMap.readObject() 会触发 key 的 hashCode() → TiedMapEntry.getValue() → LazyMap.get() → 恶意的 ChainedTransformer,最终执行任意方法(如 Runtime.getRuntime().exec("calc"))

利用链:

HashMap.readObject

HashMap.hash

TiedMapEntry.hashCode

TiedMapEntry.getValue.get

LazyMap.get

LazyMap.get.factor.transform

ChainedTransformer.transform

ConstantTransformer.transform

InvokerTransformer.transform