java代码审计11.1之反序列化基础学习

news2024/11/24 9:10:53

文章目录

  • 1、 序列化与反序列化
  • 2、序列化与反序列化案例
      • 2.1、使用idea生成代码与serialVersionUID
      • 2.2、实例化对象
      • 2.3、序列化对象
      • 2.4、反序列化
  • 3、稍微深入serialVersionUID
      • 综上小结,
  • 4、transient 作⽤
  • 5、反序列化漏洞

之前的文章,
php代码审计15.1之反序列化

php代码审计15.2之Session与反序列化

php代码审计15.3之phar伪协议与反序列化

1、 序列化与反序列化

序列化是让Java对象脱离Java运⾏环境的⼀种⼿段,
可以有效的实现多平台之间的通信、对象持久化存储。

序列化
把Java对象转换为字节序列的过程称为对象的序列化

反序列化
把字节序列恢复为Java对象的过程称为对象的反序列化。

2、序列化与反序列化案例

2.1、使用idea生成代码与serialVersionUID

在java中 序列化 必须实现 Serializable接⼝ 序列化保存的只是对象的状态,

并不包含⽅法和静态成员变量

新建⼀个user实现 Serializable接⼝

新建一个项目,
在这里插入图片描述

使用idea生成构造方法;代码--生成--构造函数--选择所有参数

在这里插入图片描述

其实可以直接右击,选择生成

在这里插入图片描述

类似的给所有的属性生成get/set方法

在这里插入图片描述

在生成一个toString方法,

toString方法将类当作字符串使用时,就会自动调用,

在这里插入图片描述

此时可以安装一个插件,直接搜索“GenerateSerialVersio”

之后就可以直接生成函数反序列化/序列号的uid

在这里插入图片描述

稍微补充下,

serialVersionUID是Java中的一个类属性,用于在序列化和反序列化过程中进行核验的一个版本号。

它是一个唯一的整数,用于标识类的版本信息。

如果两个类具有相同的serialVersionUID,则它们可以相互序列化和反序列化。

如果serialVersionUID不同,则会抛出NotSerializableException异常。 

最终的完整代码,user.java

import java.io.Serializable;

public class user implements Serializable {
    private static final long serialVersionUID = -2831602267920455150L;
    private int age;
    private String username;
    private String password;

    public user(int age, String username, String password) {
        this.age = age;
        this.username = username;
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "user{" +
                "age=" + age +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

2.2、实例化对象

新建一个main,

实例化上边的user类,这个输出的本质就是调用的user类的toString方法,

在这里插入图片描述
main代码,

package com.example.demo2;

public class main {

    public static void main(String[] args) {
        user user = new user(20,"xbb","123456");
        System.out.println(user);
    }
}

2.3、序列化对象

main.java

package com.example.demo2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class main {

    public static void main(String[] args) throws Exception {
        user user = new user(20,"xbb","123456");
//        System.out.println(user);
        serialize(user);
    }

    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }
    public static void unserialize() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser.bin"));
        user user = (user)ois.readObject();
        System.out.println(user);
    }
}

序列化后的文件是一个二进制文件,以txt的模式打开会乱码

在这里插入图片描述

以二进制的模式打开就好了,然后对比下设置serialVersionUID与不设置的区别,

红色的ser1.bin是设置的,紫色的是没有设置的(不手动设置会自动生成),

然后具体的区别也使用紫色的线划了出来,

还可以看到的是,其实序列化后保存的内容其实很有限,

在这里插入图片描述

2.4、反序列化

使用同一个serialVersionUID序列化的文件,然后反序列化是一切正常

在这里插入图片描述

但是假设反序列化读取的文件内的serialVersionUID与当前类的serialVersionUID不一致就会报错,

在这里插入图片描述

当我们注释掉类中手动设置的serialVersionUID在反序列化就一切正常

在这里插入图片描述

3、稍微深入serialVersionUID

开始说过,java的对象在序列化的时候,保存的内容有限。

很多内容不会保存,经过测试得到的结论是,序列化保存的内容如下:
	● 类名
	● 包名
	● 父类名称(未测试,猜测存在)
	● 变量名称、类型、值

其他的待测试,然后这个serialVersionUID的作用就是确认反序列化的类和代码是同一个类,

不是同一个类的话,就会报错。
当我们使用自定义的serialVersionUID时,

可以看到我们修改了toString方法,但是序列化没有保存,所以我们修改了方法,

在反序列化的时候也不会报错,

在这里插入图片描述

这还一个注意的点是,
又定义了一个新的变量“xbb”,但是序列化的内容是没有这个值的,理论是会报错的,
但是这里没有报错这里个人的猜测是因为没有校验的原因是serialVersionUID是我们自己指定的,
而具体变量的存在和值是根据serialVersionUID判断,所以没有报错,

我们使用系统自己生成的serialVersionUID来还原就会报错,
这也验证了我们上边的猜想,

在这里插入图片描述

又一个新的问题是,这个serialVersionUID的值,系统是根据什么生成的呢?

这里笔者去问了下AI,得到的回答是(下边括号里的内容是笔者测试得到的),

● 类名
● 包名
● 父类名称
● 字段名称和类型(没有值)
● 方法名称和参数类型
继续测试,既然我们可以通过指定serialVersionUID来欺骗java,

生成了一个新的变量和值没有报错,



一个场景是,

指定serialVersionUID来测试,代码在序列化的时候,

xbb变量是一个int类型的数字,内容为“111”,

我本地构造xbb的类型为字符串,内容也是“111”,会不会报错呢?



先模拟生成一个字符串的xbb,序列化保存文件的名称为“ser2.bin”

这里的背景是serialVersionUID是指定的,

在这里插入图片描述

是报错的,

在这里插入图片描述

这说明,我们通过指定serialVersionUID可以骗过系统检测,肯定是后续在还原的过程出错了,

通过二进制打开ser2.bin,猜测是这个“t..”是字符串的含义,

然后java程序在还原的时候发现序列化的内容与代码中变量类型不同导致的报错,

在这里插入图片描述

但是发现数字类型没有在bin文件中找到,是不是20太小了,来一个大的数尝试且不以“0”结尾,

但是结果还是没有在bin文件中找到,


还有就是xbb变量因为是int值,在序列化后的文件内也么有找到,

在这里插入图片描述

综上小结,

反序列化文件内的serialVersionUID必须与代码内的值一致

序列化仅仅会保存类的一些基本属性(类名,包名,变量类型和值;但是不会保存方法)

在指定serialVersionUID属性的情况下,可以在序列化的时候,少序列化一些变量,

无论是否指定serialVersionUID属性,都可以修改变量的值,但是都不能修改变量的类型

serialVersionUID系统自动生成的话,根据以下属性

	● 类名
	● 包名
	● 父类名称
	● 字段名称和类型(没有值)
	● 方法名称和参数类型

4、transient 作⽤

Java中的transient关键字,transient是短暂的意思。对于transient 修饰的成员变量,在类的实例对象	的

序列化处理过程中会被忽略。因此,transient变量不会贯穿对象的序列化和反序列化,⽣命周期仅存于

调⽤者的内存中⽽不会写到磁盘⾥进⾏持久化。
比如这里,我们使用transient来修饰xbb变量,

反序列化后的代码就没有输出“333”

在这里插入图片描述
user.java

package com.example.demo2;

import java.io.Serializable;

public class user implements Serializable {
    private static final long serialVersionUID = -2831602267920455150L;
    private int age;
    private String username;
    private String password;



    public user(int age, String username, String password) {
        this.age = age;
        this.username = username;
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    transient public int xbb = 333;
    @Override
    public String toString() {
        return "user{" +
                "age=" + age +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}' + xbb +"0000000666";
    }
}

main.java

package com.example.demo2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class main {

    public static void main(String[] args) throws Exception {
        user user = new user(2000566666,"xbb11","123456");
        System.out.println(user);
        unserialize();
        //serialize(user);
    }

    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser4.bin"));
        oos.writeObject(obj);
    }
    public static void unserialize() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser4.bin"));
        user user = (user)ois.readObject();
        System.out.println(user);
    }
}

5、反序列化漏洞

模拟重写 readObject 函数造成问题,


当你将一个对象序列化为字节流时,它的状态(即数据)被写入到一个输出流中。

而在反序列化过程中,readObject()方法的作用正是从一个源输入流中读取字节序列,

再把它们反序列化为一个对象,并将其返回,readObject()是可以重写的,可以定制反序列化的一些行为。



当作者重写了readObject函数,且在函数内存在一些危险的操作时,就可能会造成问题,

因为重写后的readObject函数就和php的魔术函数一样,会在反序列化的时候自动执行,
比如下边的代码,

开发本来的想法可能是弹出一个记事本,

但是恶意者可以先在本地序列化生成的时候,将cmd变量的值改为弹出计算器,

然后将本地序列化生成的文件让目标服务器执行,

就会弹出计算器,而非记事本

在这里插入图片描述
过程,

1、本地生成恶意序列化文件XX.bin

2、让目标服务器反序列化XX.bin

user.java

package com.example.demo2;

import java.io.Serializable;

public class user implements Serializable {
//    private static final long serialVersionUID = -2831602267920455150L;
    private int age;
    private String username;
    private String password;



    public user(int age, String username, String password) {
        this.age = age;
        this.username = username;
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    transient public int xbb = 333;
    @Override
    public String toString() {
        return "user{" +
                "age=" + age +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}' + xbb +"0000000666";
    }


    public String cmd = "notepad";
    //public String cmd = "calc";

    //重写readObject()⽅法
    private void readObject(java.io.ObjectInputStream in) throws Exception {
        //执⾏默认的readObject()⽅法
        in.defaultReadObject();
        //执⾏打开计算器程序命令
        Runtime.getRuntime().exec(cmd);
    }


}

main.java

package com.example.demo2;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class main {

    public static void main(String[] args) throws Exception {
        user user = new user(2000566666,"xbb11","123456");
        System.out.println(user);
        //serialize(user);
        unserialize();

    }

    public static void serialize(Object obj) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser4.bin"));
        oos.writeObject(obj);
    }
    public static void unserialize() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ser4.bin"));
        user user = (user)ois.readObject();
        System.out.println(user);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/892016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

部署piwigo网页 通过cpolar分享本地电脑上的图片

通过cpolar分享本地电脑上有趣的照片:发布piwigo网页 文章目录 通过cpolar分享本地电脑上有趣的照片:发布piwigo网页前言1. 设定一条内网穿透数据隧道2. 与piwigo网站绑定3. 在创建隧道界面填写关键信息4. 隧道创建完成 总结 前言 首先在本地电脑上部署…

微服务最佳实践,零改造实现 Spring Cloud Apache Dubbo 互通

作者:孙彩荣 很遗憾,这不是一篇关于中间件理论或原理讲解的文章,没有高深晦涩的工作原理分析,文后也没有令人惊叹的工程数字统计。本文以实际项目和代码为示例,一步一步演示如何以最低成本实现 Apache Dubbo 体系与 S…

k8s集群监控方案--node-exporter+prometheus+grafana

目录 前置条件 一、下载yaml文件 二、部署yaml各个组件 2.1 node-exporter.yaml 2.2 Prometheus 2.3 grafana 2.4访问测试 三、grafana初始化 3.1加载数据源 3.2导入模板 四、helm方式部署 前置条件 安装好k8s集群(几个节点都可以,本人为了方便实验k8s集…

搭载KaihongOS的工业平板、机器人、无人机等产品通过3.2版本兼容性测评,持续繁荣OpenHarmony生态

近日,搭载深圳开鸿数字产业发展有限公司(简称“深开鸿”)KaihongOS软件发行版的工业平板、机器人、无人机等商用产品均通过OpenAtom OpenHarmony(以下简称“OpenHarmony”)3.2 Release版本兼容性测评,获颁O…

探索Perfetto:开源性能追踪工具的未来之光

探索Perfetto:开源性能追踪工具的未来之光 1. 引言 A. 介绍Perfetto的背景和作用 随着移动应用、桌面软件和嵌入式系统的不断发展,软件性能优化变得愈发重要。在这个背景下,Perfetto作为一款开源性能追踪工具,日益引起了开发者…

LangChain手记 Agent 智能体

整理并翻译自DeepLearning.AILangChain的官方课程:Agent(源代码可见) “人们有时会将LLM看作是知识库,因为它被训练所以记住了来自互联网或其他地方的海量信息,因而当你向它提问时,它可以回答你的问题。有一…

centos7 yum获取软件所有依赖包 创建本地yum源 yum离线安装软件

centos7 yum获取软件所有依赖包 创建本地yum源 离线安装软件 1、以安装docker 20.10为例2、centos7 yum获取docker 20.10 所有依赖包3、创建本地docker yum源4、yum使用本地docker源 离线安装docker 1、以安装docker 20.10为例 参考链接: 添加docker 清华软件源 y…

Spring Clould 搜索技术 - elasticsearch

视频地址:微服务(SpringCloudRabbitMQDockerRedis搜索分布式) 初识ES-什么是elasticsearch(P77,P78) 1.elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能…

【论文阅读】 Model Sparsity Can Simplify Machine Unlearning

Model Sparsity Can Simplify Machine Unlearning 背景主要内容Contribution Ⅰ:对Machine Unlearning的一个全面的理解Contribution Ⅱ:说明model sparsity对Machine Unlearning的好处Pruning方法的选择sparse-aware的unlearning framework Experiments…

选择大型语言模型自定义技术

推荐:使用 NSDT场景编辑器 助你快速搭建可二次编辑器的3D应用场景 企业需要自定义模型来根据其特定用例和领域知识定制语言处理功能。自定义LLM使企业能够在特定的行业或组织环境中更高效,更准确地生成和理解文本。 自定义模型使企业能够创建符合其品牌…

Android Studio实现解析HTML获取图片URL将图片保存到本地

目录 效果activity_main.xmlMainActivityImageItemImageAdapter 效果 项目本来是要做成图片保存到手机然后读取数据后瀑布流展示&#xff0c;但是有问题&#xff0c;目前只能做到保存到手机 activity_main.xml <?xml version"1.0" encoding"utf-8"?…

《python编程基础及应用》,python编程基础及应用pdf

大家好&#xff0c;小编为大家解答python编程基础课后答案上海交通大学出版社周志化的问题。很多人还不知道python编程基础及应用课后答案高等教育出版社&#xff0c;现在让我们一起来看看吧&#xff01; 单项选择题 第一章python语法基础 1. Python 3.x 版本的保留字总数是C A…

UDP TCP 报文内容

1.UDP 2.TCP 源/目的端口号:表示数据是从哪个进程来,到哪个进程去; 32位序号/32位确认号:后面详细讲;4位TCP报头长度:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是15*460 6位标志位: o URG:紧急指针是否有效 ——urgent 紧急的 o ACK:确认号是否有…

K8S核心组件etcd详解(上)

1 介绍 https://etcd.io/docs/v3.5/ etcd是一个高可用的分布式键值存储系统&#xff0c;是CoreOS&#xff08;现在隶属于Red Hat&#xff09;公司开发的一个开源项目。它提供了一个简单的接口来存储和检索键值对数据&#xff0c;并使用Raft协议实现了分布式一致性。etcd广泛应用…

vector使用以及模拟实现

vector使用以及模拟实现 vector介绍vector常用接口1.构造2.迭代器3.容量4.增删查改5.练习 vector模拟实现1.迭代器失效2.反向迭代器3.完整代码 vector介绍 和我们原来讲的string不同&#xff0c;vector并不是类&#xff0c;是一个类模板&#xff0c;加<类型>实例化以后才…

【云原生】Docker基本原理及镜像管理

目录 一、Docker概述 1.1 IT架构的演进&#xff1a; 1.2 Docker初始 1.3 容器的特点 1.4 Docker容器与虚拟机的区别 1.5 容器在内核中支持2种重要技术 1.6 Docker核心概念 1&#xff09;镜像 2&#xff09;容器 3&#xff09;仓库 二、安装Docker 2.1 Yum安装Docker…

【NEW】视频云存储EasyCVR平台H.265转码配置增加分辨率设置

关于视频分析EasyCVR视频汇聚平台的转码功能&#xff0c;我们在此前的文章中也介绍过不少&#xff0c;感兴趣的用户可以翻阅往期的文章进行了解。 安防视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求&#xff0c;让平台在内网、专网、VPN、广域网、互联网等各…

sCrypt编程马拉松于8月13日在复旦大学成功举办

继6月在英国Exeter大学成功举办了为期一周的区块链编程马拉松后&#xff0c;美国sCrypt公司创始人兼CEO刘晓晖博士带领核心团队成员王一强、郑宏锋、周全&#xff0c;于8月13日在复旦大学再次成功举办了一场全新的sCrypt编程马拉松。 本次活动由上海可一澈科技有限公司与复旦大…

【数据挖掘】使用 Python 分析公共数据【01/10】

一、说明 本文讨论了如何使用 Python 使用 Pandas 库分析官方 COVID-19 病例数据。您将看到如何从实际数据集中收集见解&#xff0c;发现乍一看可能不那么明显的信息。特别是&#xff0c;本文中提供的示例说明了如何获取有关疾病在不同国家/地区传播速度的信息。 二、准备您的…

QT中的按钮控件Buttons介绍

目录 Buttons 按钮控件 1、常用属性介绍 2、按钮介绍 2.1QPushButton 普通按钮 2.2QtoolButton 工具按钮 2.3Radio Button单选按钮 2.4CheckButton复选按钮 2.5Commam Link Button命令链接按钮 2.6Dialog Button Box命令链接按钮 Buttons 按钮控件 在Qt里&#xff0c;…