Java Deserialization - Chapter Ⅱ
Java Deserialization - Chapter Ⅱ
S0cke3t序
在上一节中我们大致讲解了Java中反序列化的基础知识以及造成漏洞的原因,这一节中我们主要来看一下URLDNS
和HashMap
在反序列化中的利用
URLDNS
首先在java内置库中并没有叫做URLDNS
的类,这个叫法主要来源于一个java反序列化利用工具ysoserial
中的一个利用链的名称,其主要指的是java内置库中java.net.url
类
根据官方文档介绍java.net.url
类主要是用来访问URL网络资源,获取ur
l对象等相关的信息
此类实现了Serializable
接口
其中我们需要关注的只有两个方法,那就是equals
和hashcode
equals
equals
方法用于比较两个URL
对象是否相等,比较依据为URL对象具有相同的协议,是否是相同的端口,以及是否具有相同的文件和片段
除此之外如果两个主机名都可以解析成相同的IP地址,则两台主机被认为是等效的,在这一过程中equals
会对传入的url
进行DNS
解析
下面来看一下equals
的比较流程
首先在equals
中判断了传入对象是否是URL
的子类,如果符合条件则调用this.handler.equals
其中this.handler
是一个URLStreamHandler
在URLStreamHandler:equals
中简单判断字符串是否相同后调用sameFile
sameFile
中对两个URL对象的文件,端口和协议进行比较,如相同则调用hostsEqual
在hostsEqual
中调用getHostAddress
完成对主机名的解析
hashcode
hashcode
顾名思义就是计算hash
表的索引值
相比于equals
,hashcode
的流程要简单的多
同样也是调用URLStreamHandler
中的方法
但这里需要注意的是hashcode
会先判断当前hashcode
的值,如果不是-1
,会立即返回当前缓存的哈希值
在URLStreamHandler
的hashcode
方法中同样也是调用getHostAddress
解析主机名
HashMap
ok,上面我们了解url
中equals
和hashcode
两个方法的一个特性,无论是使用equals
还是hashcode
都会触发DNS
的解析
我们可以称它为一个sink
,但是sink
还是不够的,我们还需要找到一个可以触发sink
的路径,这也就是我们常说的gadget
也就是利用链
通过上一节的基础知道我们知道,要想实现反序列化就需要实现Serializable
或者Externalizable
并重写readObject
方法
而在URLDNS
最常用的一个gadget
就是java.util.HashMap
,做过java开发的应该对该类非常熟悉了
下面来看一下HashMap
的具体实现
首先HashMap
实现了Serializable
接口并重写了readObject
方法
readObject
除去前面一些初始化的代码,主要来看框内的,利用for
循环遍历序列化对象的key
和value
值并调用putVal
装填到map
中
putVal
需要五个参数分别是经过hash
方法计算的hashcode,key,value
以及两个布尔型的参数
主要来看一下hash
方法
在hash
中首先调用传入对象的hashCode
方法将返回值和返回值移位16的值做异或操作,将最后的值作为哈希值
到这里我们就可以和前面的URL:hashcode
衔接起来了
如果我们在序列化HashMap
时将key
设置为一个URL
对象,那么在使用HashMap
进行反序列化时就会调用URL
对象的hashCode
方法触发DNS解析
构造
在构造之前我们需要解决两个问题,看下图
我们在向hashmap
里put
时必定要会触发一次DNS
解析,这显然不是我们希望发生的
第二就是hashcode
问题
当我们在序列化如果按照正常put
数据当到达URL
对象hashcode
由于会触发DNS
解析,会缓存hashcode
当我们再次反序列化时hashcode
不为-1
也就无法触发DNS
解析
为解决以上问题,我们可以使用反射方式向hashmap
中填充数据,并将hashcode
初始化为-1
首先声明一个HashMap
类型为URL
对象和一个整型
随后声明一个URL
对象并将我们的payload
作为构造参数,接着通过Class.forName
拿到我们的HashMap
类对象并获取所有方法名
当方法名为putVal
时向通过invoke
反射调用putVal
向hashmap
里填充构造的数据,指定hashcode
为-1
,url
对象,键值。剩余两个参数可以从put
方法照抄过来
当然除上述方法,你还可尝试使用其他的方法
最后我们通过readObject
看一下调用过程
首先ObjectInputStream
通过正常反射调用到了HashMap
的readObject
(具体流程见上一章内容)
随后进入到hash
方法中,hash
方法则会继续调用URL
对象的hashcode
这样就接上了我们最开始URL
的hashcode
流程,触发DNS
解析
由于此链并不能达到执行命令的效果,仅仅可以触发DNS
解析,所有此链也常作为检测是否存在反序列化漏洞的测试gadget
That’s All