Java反序列化学习


Java反序列化学习

0x00前言

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

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

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

package com.Test;

public class Test1  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.Test;
import  java.io.*;

public class Test2 {
    public static void main(String[] args) {
            Test1 e = new Test1();
            e.name = "员工";
            e.identify = "General staff";
            try {
                // 打开一个文件输入流
                FileOutputStream fileOut =
                        new FileOutputStream("/Desktop/123.db");
                // 建立对象输入流
                ObjectOutputStream out = new ObjectOutputStream(fileOut);
                //输出反序列化对象
                out.writeObject(e);
                out.close();
                fileOut.close();
                System.out.printf("Serialized data is saved in /Desktop/123.db");
            } catch (IOException i) {
                i.printStackTrace();
            }
        }
    }

反序列化操作


package com.Test.testSer;
import com.Test.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.Test.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");

    }

}
}
image-20200610174743832

0x02反序列化漏洞

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

Demo
package com.Test.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");
    }


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

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

ObjectInputStream && ObjectOutPutStream
序列化对象:java.io.ObjectOutputStream –> writeObject()
反序列化对象:java.io.ObjectInputStream –>readObject()

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

Serializable
image-20200610174858478

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

1.writeExternal()
2.readExternal()
package fxlh;
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("Test");
        t.setEmail("admin@Test.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();
    }
}
}
image-20200610175101617
使用了ObjectOutputStream –> wrteObject()序列化DeserializationTest类
使用了ObjectInputStream –> readObject()反序列化了DeserializationTest类
image-20200610175023340

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

1.writeExternal()

2.readExternal()
package fxlh;

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("Test");
        t.setEmail("admin@Test.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输入流
}
}
image-20200610175136540

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

image-20200610175226704

0x3相关链接

https://xz.aliyun.com/t/2041

https://javasec.org/javase/JavaDeserialization/Serialization.html

https://xz.aliyun.com/t/2043

https://patrilic.top/2020/03/18/Java%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/


Author: Free04kSec
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Free04kSec !
 Previous
Cobalt Strike基础设施 Cobalt Strike基础设施
Cobalt Strike基础设施在基础设施方面,我们希望设置这样一个可重用且高度灵活的环境。Cobalt Strike 支持重定向,当你的 Cobalt Strike 使用的 C2 域名被销毁了,你不需要创建并启用一个新的环境,只需要替换
2020-06-20 Free04kSec
Next 
IOS端微信取证 IOS端微信取证
IOS端微信取证 1.通过备份获取数据库文件 2.在文件夹中找到备份文件 ​ 文件路径隐藏文件下,所以先显示隐藏文件Command+Shift+. ​ /Users/username/资源库/Application S
2020-05-17 Free04kSec
  TOC