原理:
根据反序列化的一般要求,首先URL类肯定是需要能实现反序列化的接口的。
而在URL类里的hashcode方法里,会先进行一个判断,如果不是-1,那么就进入URLStreamHandler类里的hashCode方法:
而在这个方法里,会执行getHostAddress方法,这是根据域名解析IP地址的函数。
结合DNS的相关知识,也就是说只要走到这个函数,就一定会发出DNS请求。
同样的,hashMap类也实现了序列化接口,
在hashMap类也重写了readObject,里面也调用了hashCode方法,也就会发出DNS请求。
所以链子是这样的:
new hashMap—>hashmap.put(new URL(“这里放url”),1)—>serialize—>unserialize发出DNS请求
问题:
但是这样写有个问题,运行代码后发现只有在序列化的时候收到了一次DNS请求,而在反序列化的时候反而收不到DNS请求,这是因为hashMap.put方法里同样也调用了hash方法:
如果不修改hashCode的值,那么我们传入的url就会触发DNS请求,并被hash编码,此后并不能触发反序列化。
思考:
结合前面hashCode的-1判断,不难想到:如果能修改hashCode的值,那么就可以绕过put时hash的触发,而在put之后再把hashCode改回来,就完成整条链子了。
解决:
我们可以通过反射,修改对象属性的值。
最后的链子就是:
new hashMap—>X—>hashmap.put(new URL(“填url”),1)—>Y—>serialize—>unserialize发出DNS请求
X 为url.getClass()获取url类—>getDeclaredField(“hashCode”)获取属性hashCode—>set(url,1234)修改hashCode的值不为-1
Y 为set(url,-1)改回hashCode的值
POC:
1 | import java.io.FileOutputStream; |
关于hashcodefield.set(url,1234);
set方法的第一个参数是属性所对应的对象,这里属性是hashcodefield,对象是url,值是1234