JAVA反序列化漏洞基础原理
JAVA反序列化漏洞基础原理
1.1 什么是序列化和反序列化?
Java序列化是指把Java对象转换为字节序列的过程;
Java反序列化是指把字节序列恢复为Java对象的过程;
1.2 为什么要序列化
对象不只是存储在内存中,它还需要在传输网络中进行传输,并且保存起来之后下次再加载出来,这时候就需要序列化技术。
Java的序列化技术就是把对象转换成一串由二进制字节组成的数组,然后将这二进制数据保存在磁盘或传输网络。而后需要用到这对象时,磁盘或者网络接收者可以通过反序列化得到此对象,达到对象持久化的目的。
1.3 ObjectOutputStream 与 ObjectInputStream类
1.3.1 ObjectOutputStream类
java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
序列化操作
一个对象要想序列化,必须满足两个条件:
-
该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会使任
何状态序列化或反序列化,会抛出 NotSerializableException 。
-
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient 关键字修饰。
示例:
Employee.java
public class Employee implements java.io.Serializable{
public String name;
public String address;
public transient int age; // transient瞬态修饰成员,不会被序列化
public void addressCheck() {
System.out.println("Address check : " + name + " ‐‐ " + address);
//此处省略tostring等方法
}
}
SerializeDemo.java
public class SerializeDemo {
public static void main(String[] args) throws IOException {
Employee e = new Employee();
e.name = "zhangsan";
e.age = 20;
e.address = "shenzhen";
// 1.创建序列化流
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("employee.txt"));
// 2.写出对象
outputStream.writeObject(e);
// 3.释放资源
outputStream.close();
}
}
将Employee对象写入到了employee.txt文件中
开头的AC ED 00 05
为序列化内容的特征
1.3.2 ObjectInputStream类
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用 ObjectInputStream 读取对象的方法:
public class DeserializeDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1.创建反序列化流
FileInputStream fileInputStream = new FileInputStream("employee.txt");
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
// 2.使用ObjectInputStream中的readObject读取一个对象
Object o = inputStream.readObject();
// 3.释放资源
inputStream.close();
System.out.println(o);
}
}
打印结果:
反序列化操作就是从二进制文件中提取对象
1.3 反序列化漏洞的基本原理
在Java反序列化中,会调用被反序列化的readObject方法,当readObject方法被重写不当时产生漏洞
public class demon {
public static void main(String args[]) throws Exception{
//序列化
//定义myObj对象
MyObject myObj = new MyObject();
myObj.name = "hi";
//创建一个包含对象进行反序列化信息的”object”数据文件
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("object"));
//writeObject()方法将myObj对象写入object文件
os.writeObject(myObj);
os.close();
//反序列化
//从文件中反序列化obj对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object"));
//恢复对象
MyObject objectFromDisk = (MyObject)ois.readObject();
System.out.println(objectFromDisk.name);
ois.close();
}
static class MyObject implements Serializable {
public String name;
//重写readObject()方法
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
//执行默认的readObject()方法
in.defaultReadObject();
//执行打开计算器程序命令
Runtime.getRuntime().exec("calc.exe");
}
}
}
此处重写了readObject方法,执行了 Runtime.getRuntime().exec()
defaultReadObject方法为ObjectInputStream中执行readObject后的默认执行方法
运行流程:
- myObj对象序列化进object文件
- 从object反序列化对象
- 调用readObject方法
- 执行Runtime.getRuntime().exec("calc.exe");
``