Loading

URLDNS 利用链分析

简介

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 方法。

image-20211116210300122

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. 入口

    image-20211116202948423

  2. 进入 URL 类的 equals 方法。

    image-20211116203050106

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

    image-20211116203121982

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

    image-20211116203145504

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

    image-20211116203241184

hashCode

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

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

    image-20211116203515091

  2. 进入 URL 的 hashCode 方法。

    image-20211116203539015

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

    image-20211116203618817

ysoserial payload 分析

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

image-20211116210742667

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

image-20211116105846039

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

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

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

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

image-20211116211904621

image-20211116211826709image-20211116211846185

一些细节

一. 反序列过程

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); 

image-20211116212925368

在开始就提到过。

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

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

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

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

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

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

image-20211116214721658

此属性被 transient 修饰。

image-20211116214737024

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

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

image-20211116225640628

四. 最后为何将 hashCode 置 -1

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

这个代码有什么作用?

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

image-20211116215801863

五. 为何选 hashCode 这条链

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

主要是为了简单与通用。

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

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

image-20211116221550848

image-20211116221627816

posted @ 2021-11-16 23:03  沉云  阅读(336)  评论(0编辑  收藏  举报