JAVA反序列化漏洞利用链之URLDNS

AI-摘要
GPT-4.0-turbo GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
JAVA反序列化漏洞利用链之URLDNS
Takake1.简介
- 在URLDNS这条链中主要是利用HashMap对象反序列化时会调用HashMap对象中Key值对象的hashCode方法,而当我们传入HashMap中Key值是一个URL对象时,则会调用URL对象的hashCode方法,执行该方法时会触发URLStreamHandler对象调用其自身的hashCode方法,其hashCode方法又会调用其自身getHostAddress方法,来解析域名,导致触发DNS解析。
- 由于该链非常通用,都是JDK自身自带的类,又由于该利用链危害性低,因此该利用链通常用于验证是否存在反序列漏洞。
2.代码分析
2.1.入口类HashMap分析
- 由于HashMap参数的灵活性可以传入两个对象,因此在多数利用链中都会用到该类。
- 我们知道在JAVA反序列化对象时会自动调用该类的readObject方法,而在HashMap的readObject方法调用了HashMap中的hash方法,而hash方法中又恰好调用了我们可控参数中的key对象的hashcode方法,因此我们可以利用该入口类来实现反序列化漏洞。
- HashMap.java
- 从图中我们可以看到,只要我们传入的key对象不为空,就可以调用key对象的hashCode方法。
2.2.执行类分析
那么入口类和入口方法找到了我们下一步就得来找执行类,那在已知加载类中有哪些类存在可以利用的hashcode方法呢。
我们可以通过IDEA自带的查找函数用法,来定位我们需要的类方法。
- 如果没找到想要的方法可以在左下角的设置中勾选重写方法。
- 通过查找类我们可以发现URL类调用了hashCode方法。
- 而URL类的hashCode方法又调用了handler的hashCode方法,这时我们可以持续定位到URLStreamHandler类,是该类调用了它的hashCode方法,且将URL对象传入到了handler的hashCode方法中,我们继续跟进
- 我们发现该方法又将url对象传入到了getHostAddress方法中,继续跟进该方法
- 我们通过方法介绍和代码可以得知该方法主要用途就是获取主机得ip地址,其实就算完成了一次dns查询。因此跟到这我们进不用继续跟进了。
- 通过梳理以上分析我们就可以得知,只要我们将URL对象作为HashMap中key值对象传入HashMap中,当反序列化时就能触发该漏洞。
- 实际情况如何呢,那么接下来我们来写一下这条链。
3.反序列化利用链编写
按照我们以上分析的,将dnslog写入URL对象中然后传入到HashMap对象中最后进行序列化再反序列化。
先生成一个dnslog域名,可以使用专业版的burpsutie,或者使用dnslog.cn来进行回显。
我们这里采用burpsutie
- 首先是序列化代码
1 | public class Serializer { |
- 然后序列化对象
1 | public class URLDNS { |
- 然后执行代码无报错,点击Poll now,查看dnslog。
- 我们发现dnslog请求成功,那么我们这条链就对了吗?
- 确定这条dns查询请求是在反序列化中请求的吗?
- 我们注释掉反序列化函数再来执行一遍。
1 | //Serializer.Deserialize("ser.bin"); |
- 我们发现也收到了dns查询请求。
- 然后我们这次注释掉其他代码,仅执行反序列化看看。
1 | Serializer.Deserialize("ser.bin"); |
- 我们发现,其实并没有收到dns查询请求,说明其实dns查询请求是在反序列化之前发送的dns查询请求,而不是在反序列化时触发的查询请求。(解释一下,上面三条,下面一条,是因为dns查询是需要进行轮询递归查询的,因此收到的dns查询条数是相对随机的,可能是一条两条四条都有可能,并不是说它调用了几次查询请求的函数。)
- 问题具体出在哪,我们来调试一下。
- 通过调试我们发现本应该在反序列化中URL.hashCode,应该调用handler.hashCode,然后在上一个if语句中就返回了,并没有调用handler.hashCode。
- 它满足了hashCode != -1这个条件。
- 通过查看URL代码我们可以发现,hashCode默认值确实是-1,但为什么在我们反序列化之前就变了呢?
- 那么我们再次进行调试,看hashCode值是在哪进行变更。
我们发现我们在调用HashMap.put的时候,HashMap也会调用一次hash(key),然后调用key.hashCode,而调用一次hashCode函数之后key的默认hashCode值-1,将会改变。从而在之后的反序列化过程中再次调用hashCode函数时,hashCode的值就不为-1了,自然无法在调用handler.hashCode。
那么有两个思路
- 第一个思路呢,我们直接通过反射给HashMap对象添加key,而不调用put方法,但是这样需要操作HashMap中的node,相对来说比较麻烦,我们就不采用这个方法了。
- 第二个思路是通过反射来修改hashCode的值(hashCode为私有属性,无法直接修改),再调用put方法之后。
具体代码如下
1 | public class URLDNS { |
- 这样在反序列化对象时就会发送dns请求。
- 如果我们为了完美的话可以在调用put方法之前也进行一次hashCode值得修改,改为不为-1,这样调用put方法时就不会走到handler.hashCode方法中,也不会触发dns查询请求,调用完put请求之后我们再将hashCode值改成-1。这样不会让我们在生成序列化对象时发送dns查询请求,不会导致误判。具体优化后的代码逻辑如下
1 | public class URLDNS { |
4.总结
- 查看调用堆栈信息
- 主要就是注意hashCode中得if条件判断,确保反序列化后的对象中的hashCode属性为值-1,就不会出现问题。
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果