Java反序列化-URLDNS链
Java反序列化-URLDNS链
正式开始学习Java反序列化,在这个过程中写点博客的同时加强理解
URLDNS链 介绍
URLDNS 是ysoserial中利用链的一个名字,通常用于检测是否存在Java反序列化漏洞。该利用链具有如下特点:
- 不限制jdk版本,使用Java内置类,对第三方依赖没有要求。所以通常用于检测反序列化的点
- 目标无回显,可以通过DNS请求来验证是否存在反序列化漏洞
- URLDNS利用链,只能发起DNS请求,并不能进行其他利用
ysoserial中列出的Gadget:
1 | * Gadget Chain: |
为什么选择HashMap作为入口类 ?
- 参数种类多并且可控
- 类可反序列化,实现了了序列化接口
- 最终走到反序列化触发的readObject
URL类
从0开始找到这条URLDNS链,先看URL类如何触发DNS请求
进入URL类查看hashCode()方法,在1109行调用
1 | public synchronized int hashCode() { |
跟进hashCode()方法,在方法内部我们能发现内部调用了getHostAddress():
1 | protected int hashCode(URL u) { |
跟进getHostAddress() 方法
1 | protected InetAddress getHostAddress(URL u) { |
继续跟进u.getHostAddress()
1 | synchronized InetAddress getHostAddress() { |
这几行代码做的事情是:
- 缓存检查:如果
hostAddress已经被解析并缓存(非null),直接返回,避免重复 DNS 查询。 - 输入校验:如果成员变量
host(应为域名或 IP 字符串,如"``example.com``")为空或空字符串,则无法解析,返回null。 - 调用
InetAddress.getByName(host)尝试将host解析为InetAddress对象。 - 如果域名不存在或网络问题,抛出
UnknownHostException,捕获后返回null。
InetAddress.getByName(host),它的作⽤是根据主机名,获取其IP地址,在⽹络上其实就是⼀次DNS查询。
总的来说就是URL类在调用其hashCode()方法能触发DNS请求
1 | public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException { |
HashMap类
选择HashMap的原因
一、有序列化接口
1 | public class HashMap<K,V> extends AbstractMap<K,V> |
二、重写了readObject方法
三、jdk原生自带
“为什么 HashMap 要自己实现 writeObject 和 readObject 方法,而不是使用 JDK 统一的默认序列化和反序列化操作呢?
首先要明确序列化的目的,将 Java 对象序列化,一定是为了在某个时刻能够将该对象反序列化,而且一般来讲,序列化和反序列化所在的机器是不同的,因为序列化最常用的场景就是跨机器的调用,而序列化和反序列化的一个最基本的要求就是:反序列化之后的对象与序列化之前的对象是一致的。
HashMap 中,由于 Entry 的存储位置是根据 Key 的 Hash 值来计算,然后存放到数组中的。对于同一个 Key,在不同的 JVM 实现中计算得出的 Hash 值可能是不同的。Hash 值不同导致的结果就是:有可能一个 HashMap 对象的反序列化结果与序列化之前的结果不一致。
即有可能序列化之前,Key=”AAA” 的元素放在数组的第 0 个位置,而反序列化后,根据 Key 获取元素的时候,可能需要从数组为 2 的位置来获取,而此时获取到的数据与序列化之前肯定是不同的。”
跟进readObject
找到HashMap的readObject 方法,在putVal()内部调用了hash()
1 | for (int i = 0; i < mappings; i++) { |
跟进HashMap重写的hash函数
1 | static final int hash(Object key) { |
我们可以看到,传入一个对象Object key,当key不为null时调用其hashCode()那么如果我们把key变成我们的url对象,理论上就可以调用url.hashcode()
那么现在就去找调用putVal()的地方,在632行的put()方法中:
1 | public V put(K key, V value) { |
因此我们需要调用HashMap的put()方法传递URL类对象为key值
1 | public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException { |
DNS请求成功





