Java反序列化漏洞

在各种编程语言的反序列化漏洞之中,Java是引人瞩目的。因为Java的开发生态中各种第三方库组件相互依赖,经常出现开发中常用的基础底层组件出现安全问题时,会引发核弹式反应,进而影响到上层得操作系统。

目录

序列化和反序列化概述

反序列化漏洞分析

Java序列化基础知识

默认序列化机制

自定义序列化机制

Java反序列化漏洞原理

Java反序列化漏洞利用--ysoserial


序列化和反序列化概述

序列化是将对象的状态信息转换成可以存储或传输的形式的过程。在序列化期间,对象将当前的状态写入到临时或持久性的存储区,简单点说就是将状态对象保存为字符串

反序列化就是把序列化之后的字符串在转化为对象的过程。

反序列化漏洞分析

在序列化过程中,其实是没有漏洞的。产生漏洞的主要原因是因为在反序列化的过程中,会自动调用一些魔法函数(例如PHP中的__construct()),如果函数的传入参数是可控的,攻击者就可以输入一些恶意代码到函数中,从而导致反序列化漏洞。

Java序列化基础知识

在Java中,只要一个类实现了java.io.Serializable接口,那么它就可以通过ObjectInputStream与ObjectOutputStream序列化。序列化的实现有两种方法。

默认序列化机制

通过writeObject方法实现对象序列化

FileOutputStream f = new FileOutputStream("date.ser");
ObjectOutputStream s = new ObjectOutputStream(f);
s.writeObject(new Date());
s.flush();

通过readObject方法实现字节流反序列化

FileInputStream in = new FileInputStream("date.ser");
ObjectInputStream s = new ObjectInputStream(in);
Date date = (Date)s.readObject();

自定义序列化机制

虽然Java序列化有默认机制,但也支持用户自定义。例如对象的一些成员变量没必要序列化保存或传输,就可以不序列化,或者是对一些敏感字段进行处理等。而自定义序列化就是重写writeObject与readObject。

例如:

Person类:

import java.io.*;

public class Person implements Serializable {
    private String name;

    public Person(String name){
        this.name = name;
    }

    @Serial
    private void readObject(ObjectInputStream s) throws IOException {
        //readObject反序列化函数重写
        System.out.println("Person.readObject method is being called");
        //从输入数据流中解码读取一个字符串,并将其设置为name属性
        name = s.readUTF();
    }

    @Serial
    private void writeObject(ObjectOutputStream s) throws IOException {
        //wireObject序列化函数重写
        System.out.println("Person.writeObject method is being called");
        //将name属性输出到数据流里
        s.writeUTF(name);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }

}

创建Person对象

public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person("hacker");

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(person);    // 对 Person 对象进行序列化
        byte[] bytes = baos.toByteArray();  // 得到序列化后得到的字节数组

        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bais);
        Person p = (Person) ois.readObject();   // 对 bytes 字节数组进行反序列化,得到 Person 对象
    }

运行结果

Java反序列化漏洞原理

到这里感觉一切都很正常,但当如果反序列过程中提供了命令执行的机会,且对来自外部输入的数据没有进行检查过滤,那么反序列化漏洞就产生了,攻击者可以进行任意命令执行。

例如:

测试代码:

import java.io.*;

public class Example implements Serializable {
    private String cmd;

    public Example(String cmd){
        this.cmd = cmd;
    }

    @Serial
    private void readObject(ObjectInputStream s) throws IOException {
        cmd = s.readUTF();                  //从输入的数据流中读取一个字符串
        Runtime.getRuntime().exec(cmd);     //这里进行命令执行
    }

    @Serial
    private void  writeObject(ObjectOutputStream s) throws IOException {
        s.writeUTF(cmd);
    }

}

攻击代码:

import java.io.*;
import java.nio.charset.StandardCharsets;

public class Testing {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Example example =new Example("calc");           //计算器命令
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(example);

        String str = baos.toString(StandardCharsets.UTF_8);     //按指定字符编码转换成字符串
        System.out.println(str);

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois =new ObjectInputStream(bais);
        Example e = (Example) ois.readObject();
    }
}

结果:

梳理一下,这里的漏洞的条件是因为对象重写了反序列化函数readObject,并且readObject方法存在执行命令的机会,所以攻击者精心构造了序列化对象,使得数据流反序列化的过程中恶意命令得以执行。

当然,在现实世界的反序列化漏洞往往很复杂,没有这么“白痴”。但也以直观的方式演示了反序列化漏洞产生的原因,其他比较复杂的反序列化漏洞的成因都与上例相似,就是漏洞利用的payload会比较复杂。

如果还有兴趣了解复杂一点的反序列化漏洞,可以看看这一篇文章反序列化漏洞,我还是个小白就不多班门弄斧。

Java反序列化漏洞利用--ysoserial

ysoserial是一款用于生成利用不安全的Java对象反序列化的有效负载的概念验证工具。

下载地址:ysoserial项目地址

使用方法:

java -jar ysoserial-master-8eb5cbfbf6-1.jar CommonsCollections5 "cmd /c calc" |base64 -w0
# 还需要进行一次 url 编码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值