JAVA安全之URLDNS链
JAVA安全之URLDNS链
一个对于新手非常友好的链子,不是很复杂
1 |
|
利用效果是发起一次远程请求,而不能去执行命令。基本上是用来测试是否存在反序列化漏洞的一个链,比如在一些无法回显执行命令的时候,可以通过URLDNS链去发送一个dns解析请求,如果dnslog收到了请求,就证明了存在漏洞。
前置知识
Java反序列化
先简单了解一下概念
Java提供了一种对象序列化的机制,用一个字节序列表示一个对象,该字节包含对象的数据、对象的类型、对象的存储属性。字节序列写出到文件后,相当于可以持久报错了一个对象信息,这过程叫做序列化。序列化对象会通过ObjectOutputStream
的writeObject
方法将一个对象写入到文件中。
而反序列化是使用了readObject
方法进行读取并还原成在序列化前的一个类。
这一步骤并没有什么安全问题,但是如果反序列化的数据是可控的情况下,那么我们就可以从某个输入点,输入恶意代码,再去查找在哪个点,我们的输入会被一层一层的带去到我们的触发点去,而这一步叫做寻找利用链的步骤。
可以反序列化必须满足以下几个条件:
1.类必须实现 java.io.Serializable
接口(最基本)
2.需要存在一个可利用的类,其readObject()方法或其他方法中存在可被利用的逻辑,例如执行命令、读取文件等
3.序列化和反序列化的类需要具有相同的serialVersionUID
Java反射
首先来说一下正射
我们在编写代码时,当需要使用到某一个类的时候,都会先了解这个类是做什么的。然后实例化这个类,接着用实例化好的对象进行操作,这就是正射。
1 |
|
反射
反射就是,一开始并不知道我们要初始化的类对象是什么,自然也无法使用new关键字来创建对象了。
1 |
|
反射的作用:让java具有动态性;修改已有对象的属性;动态生成对象;动态调用方法;操作内部类和私有方法
在反序列化漏洞中的应用:定制需要的对象;通过invoke调用除了同名函数以外的函数;通过Class类创建对象,引入不能序列化的类
具体的讲解就不过多赘述了,可以参考一下
https://www.cnblogs.com/erosion2020/p/18559481
URLDNS链利用
ysoserial项目地址:ysoserial
jdk版本低于9,并且windows自带的防火请实时保护要关闭
拉取项目源码,导入到IDEA中。
看到pom.xml知道该项目是个maven的项目,点击pom.xml 进行刷新,将缺少的依赖给下载下来
拉取完后有红的部分也不会影响正常运行
将生成的序列化payload保存,后面用一个测试demo读取文件数据再给他反序列化一下,观察dnslog请求就可以了。
这里要用项目生成的.jar文件
java -jar target\ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://sb9p8h.dnslog.cn"
java -jar target\ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://sb9p8h.dnslog.cn" > D:\test\abc.ser
反序列化测试demo
1 |
|
返回dnslog.cn点击refresh record
URLDNS链分析
HashMap结合URL触发DNS检查的思路。在实际过程中可以首先通过这个去判断服务器是否使用了readObject()以及能否执行。之后再用各种gadget去尝试试RCE。
HashMap最早出现在JDK1.2中,底层基于散列算法实现。而正是因为在HashMap中,Entry的存放位置是根据Key的Hash值来计算,然后存放到数组中的。所以对于同一个Key,在不同的JVM实现中计算得出的Hash值可能是不同的。因此,HashMap实现了自己的 writeObject和readObject方法。
因为是研究反序列化问题,所以我们来看一下它的readObject方法
然后再跟进一下hash函数
这里传入了一个对象,当对象不为空时,调用hashCode
URL实现了序列化接口
看一眼URL的hashCode是什么逻辑
这里的hashCode默认为就是-1,所以就会正常往下走,会调用handler的hashCode方法,点进去看一下
这里对传进来的做了一个处理,顾名思义就是根据域名来获取地址也就是域名解析的意思
也就是说如果我们调用了URL内的hashCode函数,我们就可以得到一个DNS请求
总结一下这个整体思路:
hashmap->readObject->hash-URL.hashcode->getHostAddress->InetAddress.getByName(host);
亲手构造
分析完这个链子,我们自己手搓一个
首先要明确HashMap.readObject()
在反序列化时会对键值对进行 put()
操作,put()
会调用 key 的 hashCode()
,如果 key 是 java.net.URL
,就会触发其 DNS 解析行为,所以我们构造一个 HashMap<URL, String>
,并通过反射让 URL.hashCode()
在反序列化时第一次被调用
先写了一份序列化的demo
1 |
|
按理来说什么也不会发生,但是可以看到,序列化的时候就已经接收到请求了
反而反序列化收不到了,那这是什么情况呢
我们跟到hashmap的put函数里面
这里为了确保键的唯一,就已经调用hash方法也就调用了hashcode
序列化调用hashcode之后就已经改变了,已经是url的hashcode,在反序列化的时候就不会执行后面了
那么该怎么改进才能避免这种情况呢
第一点,我们在序列化之前不要发起请求
第二点,我们要put之后把hashcode改回-1
所以我们要用java的反射来改变已有对象属性
1 |
|
再执行一下
这个时候序列化就查询不到了
再反序列化一下
1 |
|
到这里就已经是成功了
为了更直接的看,在这里下个断点debug一下
反序列化的时候调一下看看
当前这个状态hashcode是-1,代表我们-1赋值成功