0%

Java反序列化1:基础

java反序列化应该算是java web中最有代表性的一类安全性问题了,因为之前面试也被问到过,这两天好好研究一下。

Demo代码

参考”Java反序列化漏洞从入门到深入”一文,具体流程为:1)序列化不安全的类(攻击者所要做的);2)脆弱程序反序列化不安全的类;3)恶意代码被执行。

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
import java.io.*;

public class VulnSerialize {
public static void main(String args[]) throws Exception{
//定义myObj对象
MyObject myObj = new MyObject();
myObj.name = "hi";
//创建一个包含对象进行反序列化信息的"object"数据文件
FileOutputStream fos = new FileOutputStream("object.ser");
ObjectOutputStream os = new ObjectOutputStream(fos);
//writeObject()方法将myObj对象写入object文件
os.writeObject(myObj);
os.close();
//从文件中反序列化obj对象
FileInputStream fis = new FileInputStream("object.ser");
// AntObjectInputStream ois = new AntObjectInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(fis);
//恢复对象,反序列化使用readObject获取对象,这里调用了已经被重写的函数
MyObject objectFromDisk = (MyObject)ois.readObject();
System.out.println(objectFromDisk.name);
ois.close();
}
}

class MyObject implements Serializable {
public String name;

//重写ObjectInputStream.readObject()方法,Java会优先调用重写的readObject()方法。
private void readObject(java.io.ObjectInputStream in) throws Exception{
//执行默认的readObject()方法
in.defaultReadObject();
//执行打开计算器程序命令
Runtime.getRuntime().exec("calc");
}
}

结果分析

可以看到,VulnSerialize.main()中反序列化一个不安全的类MyObject,该类重写了ObjectInputStream.readObject()(面向对象的多态性)使其弹出计算器,又因为反序列化时,Java会调用ObjectInputStream类的readObject()方法,如果被反序列化的类重写了readObject(),那么该类在进行反序列化时,Java会优先调用重写的readObject()方法,因此造成任意代码执行。

1546052977799

注意一点是我们只能反序列化已知的类,具体来说,如果我们的程序中没有MyObject类,那么反序列化会爆出ClassNotFound异常,这也是反序列化漏洞需要构造POP的根本原因,这个在之后的文章会说。

1546144818685

防御方式

防御方法在Java反序列化漏洞从入门到深入一文中介绍了很多种,但是基本原理都是通过白/黑名单对反序列化的类进行控制。这里介绍一种由IBM的研究人员Pierre Ernst在《Look-ahead Java deserialization》中提出的重写ObjectInputStream.resolveClass()对反序列化的类进行白名单/黑名单控制的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class AntObjectInputStream extends ObjectInputStream{
public AntObjectInputStream(InputStream inputStream)
throws IOException {
super(inputStream);
}

/**
* 只允许反序列化VulnSerialize类
*/
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
if (!desc.getName().equals(VulnSerialize.class.getName())) {
throw new InvalidClassException(
"Unauthorized deserialization attempt",
desc.getName());
}
return super.resolveClass(desc);
}
}

使用AntObjectInputStream反序列化则不会弹出计算器,因为我们只允许反序列化VulnSerialize类:

1546054466567