Loading

JAVA反序列化漏洞基础原理

JAVA反序列化漏洞基础原理

1.1 什么是序列化和反序列化?

Java序列化是指把Java对象转换为字节序列的过程;

Java反序列化是指把字节序列恢复为Java对象的过程;

image-20210507210106526

1.2 为什么要序列化

对象不只是存储在内存中,它还需要在传输网络中进行传输,并且保存起来之后下次再加载出来,这时候就需要序列化技术。

Java的序列化技术就是把对象转换成一串由二进制字节组成的数组,然后将这二进制数据保存在磁盘或传输网络。而后需要用到这对象时,磁盘或者网络接收者可以通过反序列化得到此对象,达到对象持久化的目的。

1.3 ObjectOutputStream 与 ObjectInputStream类

1.3.1 ObjectOutputStream类

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

序列化操作

一个对象要想序列化,必须满足两个条件:

  • 该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会使任

    何状态序列化或反序列化,会抛出 NotSerializableException 。

  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用

    transient 关键字修饰。

示例:

Employee.java

public class Employee implements java.io.Serializable{
    public String name;
    public String address;
    public transient int age; // transient瞬态修饰成员,不会被序列化
    public void addressCheck() {
        System.out.println("Address check : " + name + " ‐‐ " + address);
        
    //此处省略tostring等方法
    }
}

SerializeDemo.java

public class SerializeDemo {
    public static void main(String[] args) throws IOException {
        Employee e = new Employee();
        e.name = "zhangsan";
        e.age = 20;
        e.address = "shenzhen";
            // 1.创建序列化流
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("employee.txt"));
            // 2.写出对象
            outputStream.writeObject(e);
            // 3.释放资源
            outputStream.close();

    }
}

将Employee对象写入到了employee.txt文件中

image-20210508144949488

开头的AC ED 00 05为序列化内容的特征

1.3.2 ObjectInputStream类

如果能找到一个对象的class文件,我们可以进行反序列化操作,调用 ObjectInputStream 读取对象的方法:

public class DeserializeDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 1.创建反序列化流
        FileInputStream fileInputStream = new FileInputStream("employee.txt");
        ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
        // 2.使用ObjectInputStream中的readObject读取一个对象
        Object o = inputStream.readObject();
        // 3.释放资源
        inputStream.close();
        System.out.println(o);
    }
}

打印结果:

image-20210508150221435

反序列化操作就是从二进制文件中提取对象

1.3 反序列化漏洞的基本原理

在Java反序列化中,会调用被反序列化的readObject方法,当readObject方法被重写不当时产生漏洞

public class demon {
    public static void main(String args[]) throws Exception{
        //序列化
        //定义myObj对象
        MyObject myObj = new MyObject();
        myObj.name = "hi";
        //创建一个包含对象进行反序列化信息的”object”数据文件
        ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("object"));
        //writeObject()方法将myObj对象写入object文件
        os.writeObject(myObj);
        os.close();

        //反序列化
        //从文件中反序列化obj对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object"));
        //恢复对象
        MyObject objectFromDisk = (MyObject)ois.readObject();
        System.out.println(objectFromDisk.name);
        ois.close();
    }

    static class MyObject implements Serializable {
        public String name;
        //重写readObject()方法
        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
            //执行默认的readObject()方法
            in.defaultReadObject();
            //执行打开计算器程序命令
            Runtime.getRuntime().exec("calc.exe");
        }
    }
}

此处重写了readObject方法,执行了 Runtime.getRuntime().exec()

defaultReadObject方法为ObjectInputStream中执行readObject后的默认执行方法

image-20210508153910511

运行流程:

  1. myObj对象序列化进object文件
  2. 从object反序列化对象
  3. 调用readObject方法
  4. 执行Runtime.getRuntime().exec("calc.exe");

``

posted @ 2021-05-11 16:17  Atomovo  阅读(3128)  评论(0编辑  收藏  举报