转载

Java反序列化学习

@Author: Patrilic @Time: 2020-3-18 11:26:55

Java反序列化学习

0x00 前言

Java中只需要实现 java.io.Serializable 或者 java.io.Externalizable 接口即可执行序列化操作

0x01 构造序列化/反序列化操作

构造一个调用类,其中,Exployee类实现了 java.io.Serializable 接口 :

package com.patrilic.testSer;

public class Employee implements java.io.Serializable
{
   public String name;
   public String identify;
   public void mailCheck()
   {
      System.out.println("This is the "+this.identify+" of our company");
   }
}

序列化操作, 在调用Employee类时,所有的数据都会被序列化

package com.patrilic.testSer;
import com.patrilic.testSer.Employee;

//import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;
import java.io.*;
public class Step1 {
   public static void main(String [] args) {
      Employee e = new Employee();
      e.name = "员工甲";
      e.identify = "General staff";
      try {
        // 打开一个文件输入流
         FileOutputStream fileOut =
         new FileOutputStream("/tmp/test.db");
         // 建立对象输入流
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         //输出反序列化对象
         out.writeObject(e);
         out.close();
         fileOut.close();
         System.out.printf("Serialized data is saved in /tmp/test.db");
      }catch(IOException i)
      {
          i.printStackTrace();
      }
   }
}

反序列化操作

package com.patrilic.testSer;
import java.io.*;
public class Step2
{
   public static void main(String [] args)
   {
	  
      Employee e = null;
      try
      {
        // 打开一个文件输入流
         FileInputStream fileIn = new FileInputStream("/tmp/test.db");
        // 建立对象输入流
         ObjectInputStream in = new ObjectInputStream(fileIn);
        // 读取对象
         e = (Employee) in.readObject();
         in.close();
         fileIn.close();
      }catch(IOException i)
      {
         i.printStackTrace();
         return;
      }catch(ClassNotFoundException c)
      {
         System.out.println("Employee class not found");
         c.printStackTrace();
         return;
      }
      System.out.println("Deserialized Employee...");
      System.out.println("Name: " + e.name);
      System.out.println("This is the "+e.identify+" of our company");


    }
}

Java反序列化学习

0x02 反序列化漏洞

Java反序列化中,会调用被反序列化的readObject方法,如果readObject方法是恶意的,那么就会引发漏洞

Demo

package com.patrilic.testSer;
import java.io.*;

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

        UnsafeClass Unsafe = new UnsafeClass();
        Unsafe.name = "hacked by ph0rse";

        FileOutputStream fos = new FileOutputStream("object");
        ObjectOutputStream os = new ObjectOutputStream(fos);
        //writeObject()方法将Unsafe对象写入object文件
        os.writeObject(Unsafe);
        os.close();
        //从文件中反序列化obj对象
        FileInputStream fis = new FileInputStream("object");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //恢复对象
        UnsafeClass objectFromDisk = (UnsafeClass)ois.readObject();
        System.out.println(objectFromDisk.name);
        ois.close();
    }
}

class UnsafeClass implements Serializable{
    public String name;
    //重写readObject()方法
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
        //执行默认的readObject()方法
        in.defaultReadObject();
        //执行命令
        System.out.println("my First!");
        Runtime.getRuntime().exec("open -a Calculator.app");
    }
}

这里进行反序列化操作时,使用 ObjectInputStream 读取序列化文件,然后调用了 readObject() , 当然就会成功的执行命令

Java反序列化学习

并且确实是先执行了readObject类的操作,然后再进行 System.out.println(name)

ObjectInputStream && ObjectOutputStream

序列化对象: java.io.ObjectOutputStream -> writeObject()

反序列化对象: java.io.ObjectInputStream -> readObject()

也就是说在序列化类时,就会自动去

Serializable

Java反序列化学习

java.io.Serializable接口是空接口,仅仅用于表示这个类可序列化

package com.patrilic.demo;
import java.io.*;
import java.util.Arrays;
public class DeserializationTest implements Serializable {
    private String username;
    private String email;
    public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	
    public static void main(String[] args) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            // 创建DeserializationTest类,并类设置属性值
            DeserializationTest t = new DeserializationTest();
            t.setUsername("Patrilic");
            t.setEmail("admin@patrilic.top");

            // 创建Java对象序列化输出流对象
            ObjectOutputStream out = new ObjectOutputStream(baos);

            // 序列化DeserializationTest类
            out.writeObject(t);
            out.flush();
            out.close();

            // 打印DeserializationTest类序列化以后的字节数组,我们可以将其存储到文件中或者通过Socket发送到远程服务地址
            System.out.println("DeserializationTest类序列化后的字节数组:" + Arrays.toString(baos.toByteArray()));

            // 利用DeserializationTest类生成的二进制数组创建二进制输入流对象用于反序列化操作
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

            // 通过反序列化输入流(bais),创建Java对象输入流(ObjectInputStream)对象
            ObjectInputStream in = new ObjectInputStream(bais);

            // 反序列化输入流数据为DeserializationTest对象
            DeserializationTest test = (DeserializationTest) in.readObject();
            System.out.println("用户名:" + test.getUsername() + ",邮箱:" + test.getEmail());

            // 关闭ObjectInputStream输入流
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

Java反序列化学习

  • 使用了ObjectOutputStream->writeObject()序列化了DeserializationTest类
  • 使用了ObjectInputStream->readObject()反序列化了DeserializationTest类

Externalizable

Java反序列化学习

java.io.Externalizable继承Serializable接口,定义了两个方法:

  • writeExternal()
  • readExternal()
package com.patrilic.demo;

import java.io.*;
import java.util.Arrays;


public class ExternalizableTest implements java.io.Externalizable {

    private String username;
    private String email;
    public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(username);
        out.writeObject(email);
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.username = (String) in.readObject();
        this.email = (String) in.readObject();
    }

    public static void main(String[] args) {
    	ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {
            // 创建DeserializationTest类,并类设置属性值
        	ExternalizableTest t = new ExternalizableTest();
            t.setUsername("Patrilic");
            t.setEmail("admin@patrilic.top");

            // 创建Java对象序列化输出流对象
            ObjectOutputStream out = new ObjectOutputStream(baos);

            // 序列化DeserializationTest类
            out.writeObject(t);
            out.flush();
            out.close();

            // 打印ExternalizableTest类序列化以后的字节数组,我们可以将其存储到文件中或者通过Socket发送到远程服务地址
            System.out.println("ExternalizableTest类序列化后的字节数组:" + Arrays.toString(baos.toByteArray()));

            // 利用ExternalizableTest类生成的二进制数组创建二进制输入流对象用于反序列化操作
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

            // 通过反序列化输入流(bais),创建Java对象输入流(ObjectInputStream)对象
            ObjectInputStream in = new ObjectInputStream(bais);

            // 反序列化输入流数据为DeserializationTest对象
            ExternalizableTest test = (ExternalizableTest) in.readObject();
            System.out.println("用户名:" + test.getUsername() + ",邮箱:" + test.getEmail());

            // 关闭ObjectInputStream输入流
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

Java反序列化学习

值得注意的是两个方法的重写:

Java反序列化学习
原文  https://patrilic.top/2020/03/18/Java 反序列化/
正文到此结束
Loading...