Commons-Collections5原理分析
CC5
依旧是 LazyMap
加 ChainedTransformer
的触发模式,只不过不再使用 AnnotationInvocationHandler
的动态代理来触发 LazyMap
的 get
,而是找到了其他的方式。
因为 jdk 在 1.8 之后对 AnnotationInvocationHandler
类进行了修复,所以在 jdk 1.8 版本就必须找出能替代 AnnotationInvocationHandler
的新的可以利用的类。
前置知识
TiedMapEntry
org.apache.commons.collections.keyvalue.TiedMapEntry
是一个 Map.Entry
的实现类,从名称中可以看到,这是一个绑定了底层 map
的 Entry
,用来使一个 map entry
对象拥有在底层修改 map
的功能。
TiedMapEntry
中有一个成员属性 Map
,这就是 Map.Entry
的底层 map
,TiedMapEntry
的 getValue()
方法会调用底层 map
的 get()
方法,我们可以用来触发 LazyMap
的 get
。
那谁会调用 getValue()
方法呢?我们发现 TiedMapEntry
的 equals/hashCode/toString
都可以触发。
equals/hashCode
让我们想到了 URLDNS
的 HashMap
,不过在 CC5
中我们用的是 toString()
方法。
接下来需要找到一个类在反序列化时会触发 TiedMapEntry
的 toString()
方法。
BadAttributeValueExpException
于是找到了 javax.management.BadAttributeValueExpException
这个类,反序列化读取 val
,当 System.getSecurityManager() == null
或 valObj
是除了 String
的其他基础类型时会调用 valObj
的 toString()
方法,完成上面 TiedMapEntry
的构造。System.getSecurityManager
是java安全管理器,默认关闭。有两种启动方式:
- 命令行参数启动
-Djava.security.manager
- 代码启动
System.setSecurityManager(new SecurityManager());
POC
1 | public class CC5 { |
这里有个细节,虽然能够直接通过构造方法赋值给val
,但在构造方法中有对入参做toString
操作,那在序列化payload时,得到的val
就是String
而不是map
了,所以只能通过反射的方式去赋值给val
。
总结
利用说明:
反序列化 BadAttributeValueExpException
的readObject
调用 TiedMapEntry
的 toString
方法,继而调用 TiedMapEntry
的 getValue
方法,然后调用了 LazyMap
的 get
方法,触发了后续的 Transformer
恶意执行链。
Gadget 总结:
1 | kick-off gadget:javax.management.BadAttributeValueExpException#readObject() |
调用链展示:
1 | BadAttributeValueExpException.readObject() |
依赖版本
commons-collections : 3.1~3.2.1
jdk 8u76 without a security manager