更新時間:2021-11-04 10:08:12 來源:動力節點 瀏覽1617次
此工具不用于查找漏洞。相反,它使用已知的 source->…->sink 技巧或其類似的特性來發現分支利用鏈或新的利用鏈。
這個工具在整個應用程序的類路徑中尋找一個鏈。
該工具執行一些合理的風險估計(污點判斷、污點轉移等)。
這個工具會產生誤報而不是漏報(其實還是會漏掉的,這是由作者使用的策略決定的,可以在下面的分析中看到)。
該工具基于字節碼分析。對于Java應用,很多時候我們沒有源代碼,只有War包、Jar包或者class文件。
此工具不會生成可直接使用的 Payload。具體的利用結構也需要人工參與。
在Java學習中,序列化是將對象的狀態信息轉換為可以存儲或傳輸的形式的過程。轉換后的信息可以存儲在磁盤上。在網絡傳輸過程中,可以是字節、XML、JSON等形式,將字節、XML、JSON等形式的信息還原為對象的逆過程稱為反序列化。
在Java中,對象序列化和反序列化廣泛應用于RMI(遠程方法調用)和網絡傳輸。
JDK(對象輸入流)
XStream(XML,JSON)
杰克遜(XML,JSON)
根森(JSON)
JSON-IO(JSON)
FlexSON(JSON)
Fastjson(JSON)
…
不同的反序列化庫在反序列化不同的類時有不同的行為。不同的“魔法方法”會被自動調用,這些自動調用的方法可以作為反序列化的入口點(源碼)。如果這些自動調用的方法調用了其他的子方法,那么調用鏈中的一個子方法也可以作為源,相當于知道了調用鏈的前端,從一個子方法開始尋找不同的分支。一些危險的方法(sink)可以通過方法的層調用來達到。
對象輸入流
比如一個類實現了Serializable接口,那么ObjectInputStream.readobject在反序列化的時候會自動找到該類的readObject、readResolve等方法。
比如一個類實現了Externalizable接口,那么ObjectInputStream.readobject在反序列化的時候會自動找到這個類的readExternal等方法。
杰克遜
ObjectMapper.readValue反序列化一個類時,會自動查找反序列化類的無參構造函數、包含基類型參數的構造函數、屬性的setter、屬性的getter等。
…
在接下來的分析中,以JDK自帶的ObjectInputStream為例。
控制數據類型 => 控制代碼
在反序列化漏洞中,如果我們控制了數據類型,我們就控制了代碼。這是什么意思?根據理解,寫了下面的例子:
public class TestDeserialization {
interface Animal {
public void eat();
}
public static class Cat implements Animal,Serializable {
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
public static class Dog implements Animal,Serializable {
@Override
public void eat() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("dog eat bone");
}
}
public static class Person implements Serializable {
private Animal pet;
public Person(Animal pet){
this.pet = pet;
}
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
pet = (Animal) stream.readObject();
pet.eat();
}
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(String[] args) throws Exception {
Animal animal = new Dog();
Person person = new Person(animal);
GeneratePayload(person,"test.ser");
payloadTest("test.ser");
// Animal animal = new Cat();
// Person person = new Person(animal);
// GeneratePayload(person,"test.ser");
// payloadTest("test.ser");
}
}
為方便起見,將所有類都寫在一個類中進行測試。在Person類中有Animal類的屬性pet,它是Cat和Dog的接口。在序列化中,我們可以控制Per的pet是Cat對象還是Dog對象,所以在反序列化中,pet.eat()inreadObject的具體方向是不同的。如果pet是Cat類對象,就不會去執行有害代碼Runtime.getRuntime().exec("calc");,但是如果pet是Dog類對象,它就會去執行有害代碼。
即使有時在聲明時為類屬性分配了特定對象,它仍然可以通過 Java 中的反射進行修改。如下:
public class TestDeserialization {
interface Animal {
public void eat();
}
public static class Cat implements Animal, Serializable {
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
public static class Dog implements Animal, Serializable {
@Override
public void eat() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("dog eat bone");
}
}
public static class Person implements Serializable {
private Animal pet = new Cat();
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
pet = (Animal) stream.readObject();
pet.eat();
}
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(String[] args) throws Exception {
Animal animal = new Dog();
Person person = new Person();
//Modify private properties by reflection
Field field = person.getClass().getDeclaredField("pet");
field.setAccessible(true);
field.set(person, animal);
GeneratePayload(person, "test.ser");
payloadTest("test.ser");
}
}
在Person 類中,您不能通過構造函數或setter 方法或其他方法為pet 賦值。該屬性在聲明時就已經定義為Cat類的對象,但是使用反射可以將pet修改為Dog類的對象,所以反序列化的時候還是會跑到有害代碼中去。
這只是我個人對作者的看法:“控制數據類型,你控制代碼”。在Java反序列化漏洞中,很多時候是利用Java的多態特性來控制代碼的方向,最終達到作惡的目的。
在上面的例子中,我們可以看到Person的readobject方法在反序列化的時候并沒有被手動調用。當 ObjectInputStream 反序列化對象時會自動調用它。作者將自動調用的方法調用為“魔術方法”。
使用 ObjectInputStream 反序列化時的幾種常見魔術方法:
Object.readObject()
Object.readResolve()
Object.finalize()
…
一些可序列化的 JDK 類實現了上述方法,并且還會自動調用其他方法(可以用作已知的入口點):
哈希表
Object.hashCode()
Object.equals()
優先隊列
Comparator.compare()
Comparable.CompareTo()
…
一些水槽:
Runtime.exec(),在目標環境中直接執行命令的最簡單直接的方式
Method.invoke(),需要正確選擇方法和參數,并通過反射執行Java方法
RMI/JNDI/JRMP等,通過引用遠程對象間接實現任意代碼執行的效果
…
以上就是關于“Java反序列化工具:Gadgetinspector First Glimpse”的介紹,大家如果想了解更多相關知識,不妨來關注一下動力節點的Java開發工具,里面對于工具的介紹有很多,大家可以多去學習一下。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習