前言
URLDNS这条链子的功能就是触发一次DNS请求,因为对第三方库没有依赖和对JDK没有要求,适合用来检测是否存在反序列化漏洞。
利用链分析
通过ysoserial
这个工具可以看到URLDNS
这条链的payload。
比较简短,可以看到,传入的参数是url,返回的是HashMap的实例对象ht,那我们来具体分析一下,这条链到底做了什么。
对一个对象进行反序列化会调用这个对象的readObject
方法,首先看看HashMap对象的readObject方法。
1 | private void readObject(java.io.ObjectInputStream s) |
最后一条语句对key执行hash方法,接下来跟进hash。
在hash方法中,会调用key的hashCode方法,也就是说,通过创建一个HashMap对象,对该对象的key传入其他任意对象,再对HashMap实例进行序列化,再将其进行反序列化时,就会触发执行任意对象hashCode方法。
那么HashMap对象的key传入什么对象才会触发一次DNS请求操作呢?ysoserial
传入的是一个URL
对象。
接下来,我们看看URL
的hashCode方法。
该方法也会调用handler
的hashCode方法,并将当前对象传入,handler
为URLStreamHandler
的实例对象。
进行URLStreamHandler
看看hashCode方法。
可以看到方法中调用了getHostAddress,传入的产生为一个URL对象,跟进getHostAddress。
调用用了URL的getHostAddress方法,跟进。
这里我们就看罪魁祸首了,InetAddress.getByName的作用就是根据主机名获取ip地址,会进行一次DNS查询。
那么至此,我们得到一个利用链
1 | HashMap->readObject |
接下来,通过上面的利用链,自己来构造一条。
1 | package com.URLDNS; |
这里将反序列化注释后,依旧会触发一次dns请求,原因是我们将URL对象放进HashMap也就是执行put操作的时候,也会执行一次hash方法,多多少少还是有点瑕疵。
规避在序列化就执行dns请求的方法也很简单,在URL类的hashCode方法中。
我们只需要在执行put前将URL对象的hashCode通过反射改成除了-1之外的其他数,然后再在序列化前将hashCode
改回-1,就能规避掉这个瑕疵。
1 | package com.URLDNS; |
这样就解决了URLDNS这条链子触发两次的情况。
ysoserial解决两次触发的方式
但是在ysoserial
中,并不算这样做的。
他写了一个继承继承了URLStreamHandler的SilentURLStreadHandler类,重写了getHostAddress
方法。
可以看到,直接返回了null,为什么这样也可以呢。我们只需要看看URL中传入的handler
参数修饰符就知道了。
transient
修饰符的作用是将这个属性就不会序列化到指定的目的地中。也就是说,HashMap在执行put方法的时候,调用的方法不是URLStreamHandler
中的getHostAddress
方法,而是自己构造继承至URLStreamHandler的SilentURLStreamHandler中重新的getHostAddress,因为返回null,所有在序列化前并不会触发dns请求。而这个方法也并不会序列化进对象,所以在反序列化的时候调用的为URLStreamHandler
中的getHostAddress
,才执行了dns请求。
参考文章
木头师傅
https://www.yuque.com/tianxiadamutou/zcfd4v/fewu54#f3b2a19f
Java安全漫谈 - 08.反序列列化篇(2)