从ysoserial中学习URLDNS利用链
前言
最近在学习java反序列化的内容,一开始从网上学习CommonsCollections
的利用链,发现挺难(是我太菜了,23333),然后就来学习一下ysoserial中最简单的例子——URLDNS
ysoserial
在正式开始分析之前,先介绍一下这个工具。github地址:https://github.com/frohoff/ysoserial
。下面就引用一段p牛的话来说明这个工具的用途。
反序列化漏洞在各个语⾔⾥本不是⼀个新鲜的名词,但2015年Gabriel Lawrence (@gebl)和Chris Frohoff (@frohoff)在AppSecCali上提出了利⽤Apache Commons Collections来构造命令执⾏的利⽤ 链,并在年底因为对Weblogic、JBoss、Jenkins等著名应⽤的利⽤,⼀⽯激起千层浪,彻底打开了⼀⽚ Java安全的蓝海。
⽽ysoserial就是两位原作者在此议题中释出的⼀个⼯具,它可以让⽤户根据⾃⼰选择的利⽤链,⽣成反 序列化利⽤数据,通过将这些数据发送给⽬标,从⽽执⾏⽤户预先定义的命令。
ysoserial的使⽤也很简单,这里就不过多赘述了,可以去github项目查看。
关于gadget
利⽤链也叫gadget chains
,我们通常称为gadget
。我理解的gadget
就相当于一条链子,从起点(入口)到终点(可能是命令执行结束的位置)的一条路径。
漏洞分析
URLDNS
是ysoserial
中⼀个利⽤链的名字。这个利用链比较特殊,因为其参数不是⼀个可以“利⽤”的命令,⽽是⼀个URL,其能触发的结果也不是命令执⾏,⽽是⼀次DNS请求。
虽然这个利用链不能执行命令,但是⾮常适合我们在检测反序列化漏洞时使⽤:
- 使⽤Java内置的类构造,没有依赖第三⽅库
- 在⽬标没有回显的时候,能够通过dnslog来判断是否存在反序列化漏洞
接下来我们来看代码。ysoserial/payloads/URLDNS.java
1 | package ysoserial.payloads; |
从上述代码中的注释中,我们可以了解到,这个反序列化漏洞跟HashMap
类有关。触发反序列化的⽅法是readObject
,所以我们可以直接进入到HashMap
类的readObject
⽅法,然后我们在第41行打断点,进入hash
方法
1 | private void readObject(java.io.ObjectInputStream s) |
hash
方法这里判断了key是否为空,不为空就调用了key的hashCode
方法并计算出值,key是一个java.net.URL
对象(key的值实际上就是域名),然后我们进入到hashCode
方法
1 | static final int hash(Object key) { |
这里判断hashCode
的值是否为-1,如果不等于-1,就返回hashCode
;反之,调用handler
(URLStreamHandler
对象)的hashCode
方法,继续跟进其hashCode
⽅法
1 | public synchronized int hashCode() { |
URLStreamHandler
对象的hashCode
⽅法如下,跟进getHostAddress
方法
1 | protected int hashCode(URL u) { |
getHostAddress
方法如下
1 | protected synchronized InetAddress getHostAddress(URL u) { |
这里关注第10行的InetAddress.getByName
方法,该方法的作⽤是根据主机名,获取其IP地址,在⽹络上其实就是⼀次 DNS查询。
所以整条gadget如下:
HashMap->readObject()
HashMap->putVal()
HashMap->hash()
URL->hashCode()
URLStreamHandler->hashCode()
URLStreamHandler->getHostAddress()
InetAddress->getByName()
要构造这个gadget
,第1步需要先初始化⼀个java.net.URL
对象,java.net.URL
对象作为keyjava.util.HashMap
中。第2步设置这个java.net.URL
对象的hashCode
为初始值-1
(这个hashCode
是私有类型,这里涉及到一些反射的知识,不过多赘述),这样反序列化时将会重新计算其hashCode
,才能触发到后⾯的DNS请求,否则不会调⽤URL->hashCode()
。
我们可以构造出exp如下
1 | package serialize; |
这里我们借助DNSLOG来验证此反序列化漏洞,可以看到成功解析(这里有一个小的tips,大家都知道DNS解析是会在本地留缓存的,所以如果想要重复验证此漏洞,每验证一次就更换一次域名)
至于payload中的最后这个类其实无关紧要的,只是ysoserial
为了防⽌生成payload的时候也执⾏了URL请求和DNS查询。
Reference
- Java安全漫谈 - 08.反序列化篇(2)