java反序列化漏洞 基础,原理和模拟实现

java基础

文件类型

.java  //文件为可编写的代码文件
.class //文件为.java 文件编译后的字节码文件
.jar //文件为压缩包,压缩了很多.class 字节码文件
.jsp //文件为 java 和 html 混写的文件

编译

可以使用命令 javac test.java 将 .java 文件编译为 .class 文件

运行

java test.class

Java 中的访问修饰符


在Java中,有四种访问修饰符,用于限定类、方法和变量的访问权限。这些访问修饰符决定了其他类是否可以访问被修饰的类、方法或变量。

public


被声明为 public 的类、方法或变量可以被任何其他类访问,无论这些类是否属于同一个包。这是最宽松的访问级别。

protected


被声明为 protected 的类、方法或变量可以被同一包中的其他类访问,以及其他包中的子类访问。它允许子类访问父类的成员,但不允许其他类访问。

default


默认,不使用关键字修饰。如果类、方法或变量没有显式地使用访问修饰符进行声明,那么它们的访问权限为默认级别。默认级别下,类、方法或变量可以被同一包中的其他类访问,但不能被其他包中的类访问。

private


被声明为 private 的类、方法或变量只能被定义它们的类内部访问,其他类无法访问。这是最严格的访问级别。
 

序列化

接口

被序列化的类在声明的时候需要实现 Serializable 接口,添加 implements Serializable 字段

import java.io.Serializable;

public class Student implements Serializable{
    public String name;
    public int age;
}

序列化步骤

1.创建FileOutputStream对象,目的时将序列化的内容输出到文件

2.创建ObjectOutputStream对象,作用是创建一个对象输出流,以便将 Java 对象序列化为字节流并写入文件中。

3.使用 writeObject 函数将对象序列化为字节流,并使用第二步创建的ObjectOutputStream对象将该字节流写入文件中。注意writeObject 函数在序列化的过程中针对的时对象本身而非针对类
 

序列化实现


新建文件创建,类,在类中定义主函数

主函数中创建 Student 类的对象 test1,并为其属性赋值

// Serlect.java
public class Select {
    public static void main(String[] args) {
        Student test1 = new Student();
        test1.name = "ZhangSan";
        test1.age = 18;
    }
}



在 Select 类中

编写一个序列化的函数,将其命名为 serialize(),并接受参数,参数为一个对象

public static void serialize(Object out) throws IOException {
    // 打开文件并执行写入操作
    FileOutputStream FileOutputStream = new FileOutputStream("gjl.bin");
    // 创建一个对象输出流
    ObjectOutputStream ObjectOutputStream = new ObjectOutputStream(FileOutputStream);
    // 将名为 test1 对象序列化为字节流,创建的 ObjectOutputStream 对象将该字节流写入文件中
    ObjectOutputStream.writeObject(out);
}

反序列化


反序列化步骤


1.创建 FileInputStream 对象,并指定文件路径,文件中读取字节流数据。
2.创建 ObjectInputStream 对象,并将其与上一步创建的FileInputStream对象关联,将读取的字节数据反序列化为 Java 对象。
3.返回或输出 Java 对象
 

 反序列化实现

在 Select 类中编写一个反序列化的函数,将其命名为 unserialize(),并接受文件路径参数

处理完报错异常后的代码如下:

public static Object unserialize(String FilePath) throws IOException, ClassNotFoundException {
    // 读取指定文件的字节流数据
    FileInputStream FileInputStream = new FileInputStream(FilePath);
    // 将字节流数据转换为 java 对象
    ObjectInputStream ObjectInputStream = new ObjectInputStream(FileInputStream);
    return ObjectInputStream.readObject();
}

反序列化漏洞

原理

  • 漏洞原因:

    对 readObject() 函数重写,经过链式调用,最终调用到了执行系统命令的函数

  • 关键点

    服务端重写 readObject() 函数

    链式调用

重写原因

如传输过程中对序列化后的数据编码、加密,数据传到 readObject() 时需要对接收的数据进行解码、解密等操作,因此会对 readObject 方法进行重写

 

漏洞模拟

可以在 Student() 类中重写 readObject() 函数模拟命令执行漏洞

// Student.java

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Student implements Serializable{
    public String name;
    public int age;

    // 当一个对象从字节流中被反序列化时,如果对象具有 readObject(ObjectInputStream in) 函数,则自动执行
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        Runtime.getRuntime().exec("calc");
        // 默认的反序列化操作
        in.defaultReadObject();
    }
}

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值