知識點
反序列化並不只存在於php,也存在各種語言中,如java、python等。
在學習java反序列化之前,我們需要知道一個基礎知識:重寫。
重寫
重寫,顧名思義就是重新改寫該方法,從而達到想要的結果。需要注意的是:
- 只有擁有繼承關係時才能重寫,也就是說,只能重寫父類的同名方法。
- 方法重寫時, 方法名與形參列表必須一致。
- 方法重寫時,子類的權限修飾符必須要大於或者等於父類的權限修飾符。
- 方法重寫時,子類的返回值類型必須要小於或者 等於父類的返回值類型。
- 方法重寫時, 子類拋出的異常類型要小於或者等於父類拋出的異常類型。 Exception(最壞) RuntimeException(小壞)
(順帶提一嘴重載,重載就是一個類裏的兩個或以上同名方法就是方法重載,兩個函數的參數類型或者參數數目必須不一樣,但函數名一致)
代碼準備
正常序列化
package com.sertest;
import java.io.*;
import java.io.Serializable;
class SerObj implements Serializable//實現Serializable接口,使其可以被序列化
{
public String name;
public int time;
}
public class ser{
public static void main(String args[]) throws Exception{
// 實例化對象
SerObj serObj = new SerObj();
serObj.name = "serobj";
serObj.time = 10;
// 以下就是序列化操作
// 打開object.ser文件
FileOutputStream fos = new FileOutputStream("object.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 使用writeObject()方法將serObj對象寫到object.ser文件
oos.writeObject(serObj);
oos.close();
fos.close();
}
}
正常反序列化
package com.sertest;
import java.io.*;
public class Unser {
public static void main(String args[]) throws Exception
{
FileInputStream fis = new FileInputStream("object.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
SerObj deSerobj = (SerObj) ois.readObject();
System.out.println(deSerobj.name);
ois.close();
fis.close();
}
}
此時的結果時正常的。
重寫readobject方法後的惡意代碼
package com.sertest;
import java.io.*;
class Evil implements Serializable
{
public String name;
public int time;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
Runtime.getRuntime().exec("calc.exe");
}
}
public class hack_test {
public static void main(String args[])throws Exception
{
Evil h2 = new Evil();
h2.name = "serobj";
h2.time = 10;
FileOutputStream fos = new FileOutputStream("hack.ser");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(h2);
os.close();
}
}
反序列化代碼:
package com.sertest;
import java.io.*;
public class Unser {
public static void main(String args[]) throws Exception
{
FileInputStream fis = new FileInputStream("hack.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Evil deSerobj = (Evil) ois.readObject();//這裏的類在反序列化的時候需要改變爲包含惡意代碼的類的名稱,這樣改是爲了看得更明白
System.out.println(deSerobj.name);
ois.close();
fis.close();
}
}
此時在反序列化時,由於惡意代碼重寫了readObject,所以在調用時調用的是已經被重寫的readObject方法,執行了惡意代碼。
本來想試行一下把這兩個打包成jar文件包,然後一個用來序列化數據一個用來反序列化數據,奈何java編程水平有限,只能做到這個地步。實際上生成惡意序列化數據的時候肯定要使用同一個類名和變量名,不然會因類名不同而報錯,那就不能成功了。
以後有時間的話看能不能寫一個簡單的鏈出來,再瞭解一下JAVA的反射機制,這樣能對相關的利用就能有一定程度的理解了。
參考文章:
https://www.cnblogs.com/Fluorescence-tjy/p/11222052.html