Java反序列化漏洞
简介
序列化就是把对象转换成字节流,便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象。Java 中的 ObjectOutputStream 类的 writeObject() 方法可以实现序列化,类 ObjectInputStream 类的 readObject() 方法用于反序列化。
下面是将字符串对象先进行序列化,存储到本地文件,然后再通过反序列化进行恢复的样例代码:
public static void main(String args[]) throws Exception {
String obj = "hello world!";
//将序列化对象写入文件 object.db 中
FileOutputStream fos = new FileOutputStream("object.db");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj);
os.close();
//从文件 object.db 中读取数据
FileInputStream fis = new FileInputStream("object.db");
ObjectInputStream ois = new ObjectInputStream(fis);
//通过反序列化恢复对象 obj
String obj2 = (String)ois.readObject();
ois.close();
}
问题在于,如果 Java 应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就可以带来任意代码执行。
所以这个问题的根源在于类 ObjectInputStream 在反序列化时,没有对生成的对象的类型做限制;假若反序列化可以设置 Java 类型的白名单,那么问题的影响就小很多了。
一些公用库,如:Apache Commons Collections 中实现的一些类可以被反序列化用来实现任意代码执行。WebLogic、WebSphere、JBoss、Jenkins、OpenNMS 这些应用的反序列化漏洞能够得以利用,就是依靠了 Apache Commons Collections。这种库的存在极大地提升了反序列化问题的严重程度,可以比作在开启了 ASLR 地址随机化防御的系统中,出现了一个加载地址固定的共享库。
利用 Apache Commons Collections 实现远程代码执行
这里,我们以 Apache Commons Collections 3 为例,来解释如何构造对象,能够让程序在反序列化,即调用 readObject() 时,就能直接实现任意代码执行。
Map 类是存储键值对的数据结构,Apache Commons Collections 中实现了类 TransformedMap,用来对 Map 进行某种变换,只要调用 decorate() 函数,传入 key 和 value 的变换函数 Transformer,既可从任意 Map 对象生成相应的 TransformedMap,decorate() 函数如下:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
Transformer 是一个接口,其中定义的 transform() 函数用来将一个对象转换成另一个对象。如下所示:
public interface Transformer {
public Object transform(Object input);
}
当 Map 中的任意项的 Key 或者 Value 被修改,相应的 Transformer 就会被调用。除此之外,多个 Transformer 还能串起来,形成 ChainedTransformer。