简介

URLDNS 这个利用链主要用来检测是否存在反序列化漏洞,有如下两个优点:

  • 使用java 内部的类进行构造,不依赖第三方库。
  • 如果目标可以出网,在目标没有回显的时候,可以用来验证是否存在反序列化漏洞。

漏洞成因

java.net.URL 这个类在进行 equals 比较和 hashCode 计算时,会调用 java.net.InetAddress 类的getByName 方法进行 dns 查询。

下面是分析所用的测试代码。

@Test
public void test2(){
    try{
        URL a = new URL("http://a.62b41v.dnslog.cn");
        URL b = new URL("http://b.62b41v.dnslog.cn");
        a.equals(b);
        URL c = new URL("http://c.62b41v.dnslog.cn");
        c.hashCode();
    }catch (Exception e){
        e.printStackTrace();
    }
}

URLStreamHandler 类的 getHostAddress 方法最终就会调用 InetAddress 类的 getByName 方法,所以后面的分析就截止到 getHostAddress 方法。

URLDNS 利用链分析

equals

java 官方文档说,如果两个 URL 对象被认为相等,则必须满足条件之一就是两个URL 的主机名可以解析到同一 ip 。

https://docs.oracle.com/javase/7/docs/api/java/net/URL.html#equals(java.lang.Object)

Two hosts are considered equivalent if both host names can be resolved into the same IP addresses;

URL.equals -> URLStreamHandler.equals -> URLStreamHandler.sameFile -> URLStreamHandler.hostsEqual -> URLStreamHandler.getHostAddress
  1. 入口

    URLDNS 利用链分析

  2. 进入 URL 类的 equals 方法。

    URLDNS 利用链分析

  3. 进入 URLStreamHandler 的 equals 方法。此处判断条件是两 ref 相等,并且 sameFile 为 true。

    URLDNS 利用链分析

  4. 进入 sameFile 中 hosts 比较部分。

    URLDNS 利用链分析

  5. hosts 比较中就会解析 dns。

    URLDNS 利用链分析

hashCode

在计算一个 URL 对象的 hash 时,一个计算因素就是 url 解析的 ip 地址。

URL.hashCode -> URLStreamHandler.hashCode -> URLStreamHandler.getHostAddress
  1. 入口

    URLDNS 利用链分析

  2. 进入 URL 的 hashCode 方法。

    URLDNS 利用链分析

  3. 调用 URLStreamHandler 的 hashCode 方法,发现其调用 getHostAddress 方法。

    URLDNS 利用链分析

ysoserial payload 分析

参考 ysoserial 生成 payload 的框架。可以看到调用的对应类的 getObject 方法。然后序列化这个对象,输出。

URLDNS 利用链分析

那么这里我们就主要关注 URLDNS 这个 payload 类的 getObject 方法。

URLDNS 利用链分析

可以看到它采取的是 hashCode 方式。从某种角度而言,hashCode 的确比 equals 更稳定,后面会探讨。

我们先分析简单的内容。再来分析复杂的。

HashMap ht = new HashMap();
URL u = new URL(null, url, handler);
ht.put(u, url);

通过调试,可以 ht.put 操作看到最终会调用 URL 对象的 hashCode 方法。

URLDNS 利用链分析

URLDNS 利用链分析URLDNS 利用链分析

一些细节

一. 反序列过程

java 反序列的特性,使得会先创建 HashMap 对象,然后再创建 URL 对象并 put 到 HashMap 中,所以这样就会触发 URL 类的 hashCode 方法。

从这个角度来讲,我们也可以使用 HashSet 来替代 HashMap 对象。类似类的有很多。

二. 生成时为什么不触发

参考第一条细节,既然如此,那么在构造 payload 时,也触发了 URL 的 hashCode 方法,为什么此时不会触发 dns 解析?

为了不在构造 payload 时触发,特地自定义了一个继承 URLStreamHandler 的类,并使用这个类对象构建 URL 对象。

URLStreamHandler handler = new SilentURLStreamHandler();
URL u = new URL(null, url, handler); 

URLDNS 利用链分析

在开始就提到过。

URLStreamHandler 类的 getHostAddress 方法最终就会调用 InetAddress 类的 getByName 方法,所以后面的分析就截止到 getHostAddress 方法。

所以此处通过重写 getHostAddress 方法,就不会触发 dns 解析。

另一个 openConnection 方法是抽象方法,必须实现。

三. 引入自定义类仍可反序列化

接上一条,SilentURLStreamHandler 这个类是我们自定义的类,既然引进了自定义类并用它的实例参与构建对象,那么按道理来说反序列化应该找不到这个类。那么序列化为什么不报错呢?

主要是由于传入的对象最终会被 transient 修饰。构造函数的内容。

URLDNS 利用链分析

此属性被 transient 修饰。

URLDNS 利用链分析

而 transient 修饰的作用,简单地说,就是让被修饰的成员属性变量不被序列化。这样,序列化的 URL 对象的 handler 变量就为 null 。也就不存在与自定义类相关联的问题。

在反序列化的过程中,会重新创建一个 URLStreamHandler ,以便正常使用功能。

URLDNS 利用链分析

四. 最后为何将 hashCode 置 -1

Reflections.setFieldValue(u, "hashCode", -1);

这个代码有什么作用?

URL 对象 hashCode 方法只有当 hashCode 等于 -1 时才会计算 hash,才会触发域名的 dns 解析。而在创建 payload 时已经计算过值,所以为了反序列化时再次进行计算触发就必须将其置为 -1。

URLDNS 利用链分析

五. 为何选 hashCode 这条链

由漏洞的成因可知,有两种触发 dns 解析方法,为什么选择 hashCode ,而不是 equals。

主要是为了简单与通用。

hashCode 触发简单,包含在 HashMap、HashSet 即可。在构建时只需要注意将第四点,通过反射将 hashCode 重设为 -1。

而 equals 链想要触发 URL 解析,对 url 有一定要求。可以看到,在走到解析域名那步,需要经过多次比对。

URLDNS 利用链分析

URLDNS 利用链分析

相关文章:

  • 2022-01-28
  • 2020-03-30
  • 2022-02-25
  • 2022-12-23
  • 2020-10-06
  • 2021-08-10
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-11-21
  • 2022-12-23
  • 2021-07-28
  • 2022-12-23
  • 2021-06-25
  • 2021-06-05
  • 2022-02-12
相关资源
相似解决方案