H4rdy's blogs - JAVA反序列化漏洞学习一(先定个小目标,比如弹个计算器)

什么是序列化和反序列化

Java序列化是指把Java对象转换为字节序列的过程,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
而Java反序列化是指把字节序列恢复为Java对象的过程。对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。

  1. 需要对某些对象进行序列化,让它们离开内存空间,保存到文件中或数据库中,比如Web服务器中的Session对象。
  2. 通过RMI或网络上传输对象的时候。

怎么实现序列化和反序列化

ObjectOutputStream类writeObject() 方法可以实现序列化;ObjectInputStream类的readObject()方法可以实现反序列化。
序列化和反序列化可以理解为“写”和“读“的操作

//写入对象内容
private void writeObject(java.io.ObjectOutputStream out)

//读取对象内容
private void readObject(java.io.ObjectInputStream in)

需要注意的:

  1. 只有实现了Serializable接口的类的对象才可以被序列化。
    • 如果该类有父类:
      • 如果该父类已经实现了可序列化接口,则其父类的相应字段及属性的处理和该类相同;
      • 如果该类的父类没有实现可序列化接口,则该类的父类所有的字段属性将不会序列化,并且反序列化时会调用父类的默认构造函数来初始化父类的属性,而子类却不调用默认构造函数,而是直接从流中恢复属性的值;
    • 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。
    • 当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化
  2. 如果该类的某个属性标识为static类型的,则该属性不能序列化。
  3. 如果该类的某个属性采用transient关键字标识,则该属性不能序列化。

序列化和反序列化实例

Information.java

//定义了Information类,该类实现了Serializable接口
public class Information implements java.io.Serializable{
    public String name;
    //构造函数,初始化时执行
    public Information(String name){ 
        this.name=name;
    }
}

SerializableDemo.java

import java.io.*;

public class SerializeDemo
{
    public static void main(String [] args)
    {
        Information e = new Information("H4rdy");
        try
        {
            //创建一个包含序列化对象的数据文件
            FileOutputStream fileOut = new FileOutputStream("/tmp/information.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            //将序列化的内容写入文件
            out.writeObject(e);
            out.close();
            fileOut.close();
            System.out.printf("Serialized data is saved in /tmp/information.ser");
        }catch(IOException i)
        {
            i.printStackTrace();
        }
    }
}

将Information类序列化后保存到/tmp/information.ser文件中,information.ser文件中的内容:

aced 0005 是Java序列化的特征,流量中可以根据该特征发现序列化痕迹.

DeserializableDemo.java

import java.io.*;
public class DeserializeDemo
{
    public static void main(String [] args)
    {
        Information e = null;
        try
        {
            FileInputStream fileIn = new FileInputStream("/tmp/information.ser");
            ObjectInputStream ins = new ObjectInputStream(fileIn);
            //读取字节序列并反序列重建对象
            e = (Information) ins.readObject();
            ins.close();
            fileIn.close();
        }catch(IOException i)
        {
            i.printStackTrace();
            return;
        }catch(ClassNotFoundException c)
        {
            System.out.println("Information class not found");
            c.printStackTrace();
            return;
        }
        System.out.println("Deserialized Information...");
       //恢复对象
        System.out.println("Name: " + e.name);

    }
}

读取information.ser并反序列化。

反序列化漏洞基本原理

重写readObject方法,从而执行自己的代码。

Information.java

import java.io.IOException;

public class Information implements java.io.Serializable{
    public String name;
    //重写readObject方法
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
        //执行默认的readObject()方法以便完成反序列化操作
        in.defaultReadObject();
        //执行打开计算器程序命令
        Runtime.getRuntime().exec("open /Applications/Calculator.app/");
    }

    public Information(String name){ //构造函数,初始化时执行
        this.name=name;
    }
}

tagged by none  

Post a new comment

© H4rdy's blog