URLDNS反序列化链分析
这是正式学习的第一条java反序列化链,这条链也是java里面相对比较简单的链,拿来入门正好。
URLDNS这条链不依赖第三方库,这条链无法执行系统命令,成功利用的结果也只是实现服务器的一次url解析,可以用这条链来判断是否服务器是否存在反序列化。
ysoserial
说到java反序列化,就离不开ysoserial工具,这个工具可以帮助生成反序列化payload。ysoserial入口为ysoserial.GeneratePayload
类,当然这是正常用法。
这里只看URLDNS这条链的话,来到ysoserial.payloads.URLDNS
这个类上,这个类的main方法如下
跟进到PayloadRunner的run方法,可以看到这里就是通过反射调用URLDNS的getObject方法获得一个对象,然后把这个对象进行序列化再反序列化,大概的过程就是这样,下面分析URLDNS链的执行过程
利用URLDNS链的时候,参数如下设置,注意第一个框要写大项目路径,不然会显示找不到类
过程分析
通过上面的ysoserial工具分析,可以知道URLDNS链序列化和反序列化的是HashMap对象,那么直接跟进到HashMap的readObject方法
前面的代码不重要,直接看到,readObject方法的最后代码块,调用了HashMap.hash
方法,参数为HashMap的键
继续跟进HashMap.hash
方法,调用key.hashCode
方法,在ysoserial中,HashMap的键为URL类,那么调用的就是URL.hashCode
方法
继续跟进URL.hashCode
方法,这个调用handler.hashCode
方法,在工具中handler为SilentURLStreamHandler类,这个类继承URLStreamHandler类,那么调用的就是URLStreamHandler.hashCode
方法
跟进到URLStreamHandler.hashCode
方法,调用getHostAddress方法,参数为HashMap存储的url对象
继续跟进getHostAddress方法,其调用getByName方法进行URL解析,在dsnlog中就能收到解析记录,到这里这条链就结束了。需要注意的是前面还判断了hostAddress是否存在,如果之前解析过这个域名,那么hostAddress是存在的,就无法调用getByName进行解析URL了。
注意点
-
为什么要用SilentURLStreamHandler类,而不直接用URLStreamHandler对象?这里先看到getObject方法里面有HashMap的put方法,很巧的是这个方法就调用了触发反序列化的
hash(key)
方法,所以为了防止在生成payload的时候解析URL,就写一个类继承URLStreamHandler类并重写getHostAddress方法。
-
注意到getObject方法中还通过反射把URL类的hashCode属性设置为-1,这是为什么呢?看到调用链的
URL.hashCode
方法,判断如果hashCode属性值不等于-1则直接返回了,剩下的链就无法执行了
在URL类中hashCode属性值默认就是-1,但是在getObject方法中调用了HashMap的put方法,就相当于在 这里就调用了一次调用链了,只不过这条链因为重写getHostAddress方法最终没有解析URL罢了,调用了一 次调用链那么hashCode属性值就改变了,所以就有了利用反射把URL类的hashCode属性设置为-1的操作, 用反射是因为hashCode属性是私有的 -
最后一个就是URL类中的handler属性是transient修饰了,由transient修饰的属性无法被序列化和反序列化, 那么为什么还是成功解析URL呢?这就需要看到URL类的readResolve方法,这个方法在反序列化的时候执行。这个方法先通过getURLStreamHandler获取到一个URLStreamHandler,再通过setDeserializedFields把URL类的handler属性设置为URLStreamHandler对象,这样反序列化出来的HashMap中的URL类的handler属性就不会为空了!