URLDNS链
前言
在测试反序列化的时候 通常会使用无危害的方法尝试检测是否存在反序列化漏洞
URLDNS链就是这样的一个链子 当它触发的时候 只能使得服务器自动解析我们自己输入的域名
我们可以查看域名是否被解析过 来判断是否存在反序列化漏洞
URLDNS链构造
我们可以先研究一下ysoserial中的URLDNS的调用链

实际的调用链相当简单 没有很长
通过触发hashmap中的readobject() 来触发反序列化
最后会触发URL.hashCode()方法 我们先来看一下这个方法

这个方法会判断hashCode的值 如果不为-1的话会直接返回hashCode的值
创建URL实例 会自动的将对象中的hashCode的值赋为-1

如果为hashCode为-1的话 会进入handler.hashCode()方法

handler.hashCode()方法中会调用getHostAddress() 方法 里面的参数u是传入的URL对象
getHostAddress()方法会尝试解析域名 因此会查询dns 当成功执行的时候 我们就能看到这个解析记录了
调用的时候 要求最后在反序列化的时候URL对象的hashCode的值要为-1
调用链目前是这样的情况
我们回到源码来看一下

最重要的是这个getObject()方法 它会返回最终我们需要的对象
我们一步一步来看
1 | URLStreamHandler handler = new SilentURLStreamHandler(); |
这里实例化了一个SilentURLStreamHandler对象

这个类继承自URLStreamHandler
根据刚刚看的调用链的情况 后面会触发handler.hashCode() 这里的handler就是SilentURLStreamHandler对象
至于为什么要重写getHostAddress() 后面会知道
1 | HashMap ht = new HashMap(); |
这个部分就是实例化了hashmap 和 URL对象
URL对象中传入了我们的url和handler
url是我们自己输入的域名
我们跟进一下hashmap的put方法
put方法最后的结果就是将传入的key和value给放到hashmap中

这个put方法又会调用hash()方法

判断key是否为null之后 又调用了key中的hashCode方法 也就是URL中的hashcode

这个方法首先检查对象中的hashCode是否为-1
如果为-1就会调用handler.hashCode()方法
这里的handler.hashCode()方法也就是SilentURLStreamHandler.hashCode()方法
SilentURLStreamHandler是URLStream的子类
我们刚刚就看了handler.hashCode()方法 最后调用SilentURLStreamHandler.getHostAddress(u) 进行域名解析
但是我们要让他在反序列化的时候才去域名解析 所以ysoserial中才会重写getHostAddress方法 使得这次的调用不会去解析域名
虽然重写getHostAddress方法可以绕过这次的解析 但是反序列化的时候不是也不会调用吗?
我们可以看一下URL类中的handler这个成员属性的修饰符是transient

如果一个成员被修饰为transient
那么他在序列化的时候就不会将他序列化
在反序列化的时候 通常继承serialized的类都会重写readObject方法
虽然确实不会被反序列化还原出来 使得成员值为null 但是在URL中的readobject中

会执行getURLStreamHandler()方法

会自动创建一个URLStreamHandler对象
所以实际上最终handler还是有实例化对象的
这就可以使得最后反序列化的时候调用的getHostAddress是URLStreamHandler的方法 也就可以成功解析dns了
最后一部分就是设置URL对象中的hashCode的值为-1 确保之后反序列化的时候可以成功执行getHostAddress方法

为什么这里要显示的修改hashCode的值呢?
通过调试代码可以知道

当调用hashmap的put方法之后 URL对象的hashcode的值变成了一个很奇怪的值
不难发现

在调用URL.hashCode()方法的时候 hashcode的值发生改变了
因此最后的部分一定要显式的修改hashcode的值
至此URLDNS链就成功构造了
URLDNS触发
我们现在来看一下具体是怎样触发的
当调用input的readobject方法时 会自动的调用hashmap的readobject方法


hashmap的readobject又会自动反序列URL对象

反序列化URL对象之后就会调用putVal() -> hash()

之后又会调用URL的hashCode()

最后调用handler的hashcode

最后判断hashcode的值为-1 调用到gethostaddress就成功执行dns解析了
至此URLDNS就触发完毕了
说些什么吧!