Java URL DNS 反序列化利用链——从原理到检测
前言
Java 反序列化漏洞的利用通常依赖精心构造的 gadget chain(利用链)。在实战中,红队面临的首要问题往往不是”怎么拿 shell”,而是——目标到底有没有反序列化漏洞?URL DNS 这条利用链是 ysoserial 中最短、最通用的一条,它不执行命令、不写文件,只做一件事:触发一次 DNS 查询。正因如此,它是检测反序列化漏洞是否存在的最佳探针。
整条链的核心调用路径只有五步:
1 | HashMap.put() -> hash() -> URL.hashCode() -> URLStreamHandler.hashCode() -> getHostAddress() |
下面我们从源码级逐层追踪这条链,理清每个环节的逻辑,然后给出完整的 POC 和实战用法。
调用链完整追踪
起点:HashMap.put()
HashMap 实现了 Serializable 接口,其 readObject() 方法在反序列化时会重建 map,对每个 entry 调用 putVal(),而 putVal() 内部会先对 key 做一次 hash():
1 | // HashMap.java |
这意味着:**只要我们把一个 URL 对象作为 HashMap 的 key 放进序列化数据里,反序列化时它就一定会被 hash()**。
第二步:HashMap.hash()
hash() 的实现非常精简,直接调用 key 的 hashCode():
1 | // HashMap.java |
到这里,调用权从 HashMap 移交到了 URL。
第三步:URL.hashCode()
这是整条链最关键的一个方法,也是理解”为什么要用反射改写 hashCode”的入口:
1 | // URL.java |
逻辑很清楚:hashCode 字段相当于一个懒加载缓存。初始值 -1 表示”未计算”,一旦计算过就会被赋值为一个非 -1 的正整数。之后的调用都走缓存,不再重新计算。
第四步:URLStreamHandler.hashCode()
handler 是 URL 内部的 transient URLStreamHandler 实例。它的 hashCode(URL u) 方法负责生成 URL 的哈希值——但在这个过程中,它会尝试解析主机地址:
1 | // URLStreamHandler.java |
第五步:getHostAddress() — DNS 查询触发点
getHostAddress(u) 方法内部会调用 InetAddress.getByName(host),这正是执行 DNS 解析的地方。对于一个指向 DNSLog 平台的域名(如 http://558umk.ceye.io),这次解析请求会被 DNSLog 平台记录下来,从而确认目标主机执行了反序列化操作。
整条链走完。总结调用关系:
1 | ObjectInputStream.readObject() |
完整漏洞 POC
基于以上分析,可以构造出最终的 Proof of Concept:
1 | package org.example; |
为什么 hashCode = -1 是关键
这是整条利用链中最精妙、也最容易产生误解的地方。我们用一个问题来拆解:
为什么构造 POC 时要先用反射把 hashCode 设成非 -1,put 完再改回 -1?
答案分两层:
第一层:如果不做任何处理直接 map.put(url, "114") 会怎样?
1 | HashMap map = new HashMap(); |
因为 URL 对象创建时 hashCode 默认值为 -1,put 内部调用 hash() -> hashCode(),此时 hashCode == -1 为 true,于是执行 handler.hashCode(this) -> getHostAddress(),在构造阶段就发出了一次 DNS 请求。并且在 handler.hashCode(this) 返回后,URL 内部的 hashCode 被赋值为计算结果(一个正数),不再是 -1。
这意味着:序列化到文件里的 URL 对象,其 hashCode 字段已经是有值的了。反序列化时 URL.hashCode() 发现 hashCode != -1,直接返回缓存值,**根本不会进入 handler.hashCode()**——DNS 请求不会再次发出,利用链就断了。
第二层:反射操作的目的是什么?
| 步骤 | 操作 | hashCode 值 | 效果 |
|---|---|---|---|
| URL 创建后 | (默认) | -1 | 处于”未计算”状态 |
| 反射 set(1213) | 改成一个非 -1 的值 | 1213 | 骗过 put() 里的检查,构造阶段不触发 DNS |
| map.put(url, “123”) | 正常 put | 1213 | 哈希计算走缓存,不进入 handler |
| 反射 set(-1) | 改回 -1 | -1 | 重置为”未计算”状态 |
| serialize() | 写入文件 | -1 | 序列化的对象 hashCode = -1 |
| deserialize() | 反序列化读取 | -1 | hashCode == -1 → 进入 handler.hashCode() → DNS 触发! |
核心思路:序列化之前骗过 put(),序列化时让对象保持”未计算”状态,反序列化时自然触发。
这里还有一个细节值得一提:URLStreamHandler handler 字段被声明为 transient,这意味着它不会参与序列化。反序列化时 JVM 会根据 URL 的协议(http)自动重新关联一个 Handler,所以完全不影响利用链的执行。
实际应用场景:确定目标是否存在反序列化漏洞(DNS pingback)
URL DNS 这条链最核心的实战价值在于检测反序列化漏洞是否存在。这在渗透测试中通常被称为 DNS pingback 技术。
为什么用它而不是直接上命令执行?
反序列化漏洞的利用链往往依赖于具体的类路径(classpath)环境。不同中间件、不同 JDK 版本、不同依赖库,可用的 gadget chain 完全不同。如果一个目标接收序列化数据但内部类不匹配,你精心构造的命令执行 payload 可能悄无声息地失败——而且你完全不知道是”没有漏洞”还是”链不匹配”。
DNS 查询则不同:
HashMap和URL是 JDK 内置类,从 Java 1.2 开始就存在- 这两个类都不依赖任何第三方库
- DNS 协议走 UDP 53,出站流量在绝大多数网络环境中默认放行
- DNSLog 平台(如 ceye.io、dnslog.cn、Burp Collaborator)提供免费、实时的查询记录
因此,URL DNS 是所有反序列化探测中最通用、噪音最小、成功率最高的方式。
操作步骤
- 在 DNSLog 平台获取一个唯一的子域名,例如
abc123.ceye.io - 使用上面的 POC 代码,将 URL 中的域名替换为自己的 DNSLog 地址
- 将生成的序列化数据(如
2.ser)以目标应用预期的方式发送过去(HTTP 请求体、Base64 编码后放入某个参数、甚至直接发给一个接受序列化数据的端口) - 在 DNSLog 平台刷新,如果看到了 DNS 解析记录,则确认目标存在反序列化漏洞
如果 DNS 记录出现了,说明整条链在你发送的数据进入 ObjectInputStream.readObject() 之后被成功触发——这意味着你可以进一步尝试命令执行的 gadget chain。
防御与检测
开发者视角:防御
- 根本措施:避免反序列化不可信数据。 如果业务不需要接收序列化对象,就不要用
ObjectInputStream。 - 使用白名单校验。 JDK 提供了
ObjectInputFilter(Java 9+)或可通过jdk.serialFilter系统属性全局配置,只允许反序列化指定类型的对象。 - 升级 JDK 版本。 较新版本的 JDK 对反序列化增加了更多内置限制和警告机制。
- 使用替代序列化方案。 JSON、Protobuf 等格式不受 Java 原生反序列化 gadget chain 的影响。
防守者视角:检测 DNS 出站探测
- 监控内网主机的 DNS 出站流量。 正常的业务服务器不应该频繁查询随机子域名。如果一台 Web 服务器突然尝试解析
abc123.ceye.io这种明显是 DNSLog 平台的域名,高度可疑。 - 维护 DNSLog 平台域名特征库。 在 SIEM 或 DNS 安全产品中配置对以下域名的告警:
*.ceye.io、*.dnslog.cn、*.burpcollaborator.net、*.interact.sh等。 - 网络层面限制。 如果没有特殊需求,生产环境服务器应只向受信任的 DNS 服务器发起解析,并限制出站 DNS 请求的目的域名范围。
- 应用层面日志。 在反序列化入口处增加日志,记录反序列化的来源 IP、目标类和调用栈,便于事后溯源。
参考
- ysoserial: https://github.com/frohoff/ysoserial — URLDNS 是该工具中最基础的一条链
- Java Object Serialization Specification: https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
- DNSLog 平台:
- http://ceye.io/
- http://dnslog.cn/
- Burp Suite Collaborator