Java代码审计:Java反序列化入门之URLDNS链
0x00 前言
自学Java 代码审计,主要自己一个人学习,有点闭门造车,搜索引擎学习法,但是还是记录一下,也分享一下,也便于将来的总结和反思,如果我能终能学到什么,我也会重新梳理思路,为那些自学者提供一个好的思路,所以有了下面的系列文章java代码审计自学篇。
之前研究了,但是没有整理好,因为有hvv任务,所以推迟了一些,现在赶紧整理发出来。
0x01 Java反序列化介绍
Java反序列化漏洞的产生原因:
简单的说就是,在于开发者在重写 readObject 方法的时候,写入了漏洞代码。
序列化和反序列化本身并不存在问题
但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
0x02 反序列化方法的对比
在接触Java反序列化之前,就是PHP了,PHP的反序列化和着还是有区别的
他们最基本的原理是类似的,反复横跳,找到一个利用链。
都是用于数据存储的一个格式化的操作。
将一个对象中的属性按照某种特定的格式生成一段数据流,在反序列化的时候再按照这个格式将属性拿回来,还原成对象。
而Java其提供了更加高级的writeObject ,允许在序列化流中插入一些自定义数据,进而在反序列化的时候能够使用 readObject 进行读
0x021 PHP反序列化
- 我们熟悉的都是那些魔术方法,去触发魔术方法,构造pop链。
- wakeup 魔术方法的目的就是在序列化、反序列化的前后执行一些操作。
- PHP的序列化、反序列化是低层过程,PHP的序列化是开发者是不能操作的。
- 开发者调用 serialize 函数后,序列化的数据就已经完成。
- 魔术方法只是在序列化前后对这个对象的操作。
0x022 JAVA反序列化
- Java在序列化时一个对象,就不一样了,Java是在序列化的中间,去触发一下利用链,而不是php在序列化完成后
- Java在序列化时,将会调用这个对象中的 writeObject 方法
- 反序列化时,会调用readObject
- writeObject和readObject 方法开发者多种情况都是自己会重写,造成一些问题,构造触发链。
0x03 ysoserial 介绍
15年的Apache Commons Collections 反序列化远程命令执行漏洞 (ysoserial 的最早的 commit )
同时无数 Java 应用系统各种rce疯狂爆出
反序列化漏洞利⽤⼀个⾥程碑式的⼯具,ysoserial。
最开始我都以为,这是一个特定的利用工具,后来发现,为啥好多漏洞都用这个,才知道这是一个通用的工具。
ysoserial集合了各种java反序列化payload,它可以⾃⼰选择的利⽤链,⽣成反序列化利⽤数据,通过将这些数据发送给⽬标,从⽽执⾏命令。
0x04 URLDNS 原理
这个是ysoserial中最简单的一条利用链了,也常常作为检测反序列化的功能,URLDNS这个pop链的大概的工作原理:
1、 java.util.HashMap重写了readObject方法:
在反序列化时会调用 hash 函数计算 key 的 hashCode
2、java.net.URL对象的 hashCode 在计算时会调用 getHostAddress 方法
3、getHostAddress方法从而解析域名发出 DNS 请求
0x05 构建漏洞代码
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java
ysoserial生成的代码,原理是一样的,下面是参考原理改写的一段,自己调试学习
反序列化的第一步,你得接受对象,然后反序列化吧
那么我们采用本地写一个序列号数据测试
1 | import java.io.FileOutputStream; |
随后,开始反序列化,触发漏洞
1 | import java.io.FileInputStream; |
触发
先运行第一个,生产序列化数据
再运行第二个,反序列化触发漏洞
最后收到dnslog
注意:每次换DNSLOG的时候,都有重新生产序列化数据
0x06 代码分析
先看一下利用连,熟悉一下路径
1 | HashMap->readObject() 反序列化点触发 |
梦开始的地方是HashMap 类
我们前⾯说了,触发反序列化的⽅法是 readObject
找到HashMap 类的 readObject ⽅法
触发点在 1413行的
1 | putVal(hash(key), key, value, false, false); |
然后就是,其中的第一个参数 hash(key) 方法
1 | static final int hash(Object key) { |
这里就是换对象了,这里的key肯定不是null,那么就会触发 key.hashCode()
这里使⽤的这个key是⼀个 java.net.URL 对象,我们看看其 hashCode ⽅法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3LQziqw-1597834189373)(/Users/zy/Library/Application Support/typora-user-images/image-20200813212359358.png)]
URL对象再次跟入 继续跟进其 hashCode ⽅法
359行 getHostAddress 方法传入了 DNSLOG地址
再跟入,其实就是442行,直接一句发出请求了
现在运行前
这剧请求了URL,发出了请求
收到DNS请求,成功触发URLDNS整个功能
0x07 坑点:
新手第一次调试,总找不到触发点,还以为不在这,尤其是这个地方
这里有好几个对象, 要循环好几次,所以要找到 URL对象,再步入参数的hash函数