顶部左侧内容
百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 在线教程 > 正文

告别脚本小子系列丨JAVA安全(5)——序列化与反序列化

gosiye 2024-08-26 13:55 4 浏览 0 评论

前言

告别脚本小子系列是本公众号的一个集代码审计、安全研究和漏洞复现的专题,意在帮助大家更深入的理解漏洞原理和掌握漏洞挖掘的思路和技巧。系列课程包含多篇文章,往期内容如下:

往期内容回顾

1

告别脚本小子系列丨JAVA安全(1)——JAVA本地调试和远程调试技巧

2

告别脚本小子系列丨JAVA安全(2)——JAVA反编译技巧

3

告别脚本小子系列丨JAVA安全(3)——JAVA反射机制

4

告别脚本小子系列丨JAVA安全(4)——ClassLoader机制与冰蝎Webshell分析


0x01 概述

反序列化漏洞是java安全中最常见的漏洞之一,学习反序列化漏洞的相关知识有助于帮我们掌握更多关于java安全系统性内容。序列化是指把内存中的对象转化为字节序列,主要用于在不同程序之间传递和存储对象。而反序列化是指把字节序列重新转化为对象。例如,weblogic通过t3协议来和其他java程序之间传输数据,数据传输的方式就是通过序列化和反序列化来实现的,这也是导致weblogic经常爆出反序列化漏洞的根本原因。


0x02 序列化详解

首先来看java中最典型的序列化和反序列化的一段代码如下。我把相关代码的解释都放在注释里面,方便大家查看。

import java.io.*;

class User implements Serializable{
private String name;
public User(String name) {
this.name = name;
}
// 方便打印查看类的信息
@Override
public String toString() {
return "User{name=" + name + '}';
}
}
public class Demo1 {
public static void main(String[] args) throws Exception {
User user = new User("zhangsan");
String filename = "user.ser";
serialize(filename, user); // 把对象序列化保存到文件

User user1 = (User) unserialize(filename); // 从文件反序列化对象
System.out.println(user1);

}
// 序列化对象并保存到文件
public static void serialize(String filename, Object obj) throws Exception{
// 创建一个FIleOutputStream
FileOutputStream fos = new FileOutputStream(filename);
// 将这个FIleOutputStream封装到ObjectOutputStream中
ObjectOutputStream os = new ObjectOutputStream(fos);
// 调用writeObject方法,序列化对象到文件user.ser中
os.writeObject(obj);
}
// 从文件反序列化对象
public static Object unserialize(String filename) throws Exception{
// 创建一个FIleInutputStream
FileInputStream fis = new FileInputStream(filename);
// 将FileInputStream封装到ObjectInputStream中
ObjectInputStream oi = new ObjectInputStream(fis);
// 调用readObject从user.ser中反序列化出对象,还需要进行一下类型转换,默认是Object类型
return oi.readObject();
}
}

上述代码定义了一个User类用于测试序列化和反序列化的过程。首先并不是所有的类都是可以进行序列化和反序列化的,要进行序列化和反序列化则该类必须继承自java.io.Serializable接口(该类的全部属性也必须继承自Serializable接口)。否则会抛出NotSerializableException报错,如图2.1所示。

图2.1 如果不继承Serializable接口则会抛出异常

序列化和反序列化的过程都是基于字节流来完成的。序列化是通过writeObject方法来把类对象转换为字符输出流,上述demo则是把User对象转化为文件字符输出流并保存成文件;反序列化是通过readObject方法来把字符输入转化为类对象,上述demo则是通过读取文件内容转化为User对象。在序列化和反序列化的过程中有两点需要注意:

1) 类对象序列化之后不一定要保存成文件,也可以通过ByteArrayOutputStream保存为字节数组。

2) 反序列化之后返回的数据类型为Object类型,如果要转化为序列化之前的类,需要进行强制类型转化。

运行上面的Demo代码,会在当前项目根目录生成序列化之后保存的文件user.ser文件,通过xxd可以查看文件的16进制编码,如图2.2所示。目前大部分的序列化之后的数据格式都是aced 0005,其中aced代表序列化协议,0005代表序列化协议版本。这个可以作为判断字符流是序列化数据的依据。

图2.2 查看序列化数据的16进制格式

一般来说,序列化之后的数据是不允许修改的,但是可以允许在不改变字符长度的情况下对属性值进行替换。如图2.3所示,可以把“zhangsan”替换为“lisi ”,用空格来补齐字符个数。

图2.3 对序列化之后的字符进行字符替换


0x03 反序列化漏洞

反序列化漏洞是指在反序列化过程中自动执行类中readObject方法导致的漏洞,类似于PHP反序列化时会自动执行__wakeup方法一样。为了更清晰的认识反序列化漏洞的原因,我们把上面的代码稍微改一下,如图3.1所示。

图3.1 在反序列化的类中增加readObject方法


通过上面的代码可以看出,如果readObject中执行了某种危险的操作,就可能到做反序列化漏洞,如图3.2所示。

图3.2 通过反序列化执行恶意操作


当然在实际环境中不可能有这么简单的情况,这里只是阐述一些原理性的东西。真实的环境下一定是一种利用链的调用关系,我们现在只是简单描述一下关于反序列化利用链,真实的利用链将在后续的课程中进行详述。反序列化利用链是一种链式调用逻辑,如图3.3所示。

图3.3 反序列化调用链原理


反序列化调用链从本质来说就是构造一条从反序列化入口Source到危险方法Sink的调用链。反序列化的Source点一定是从readObject方法开始,但是Sink点却可以有很多种不同的类型。最常见的反序列化Sink是命令执行,但是也有可能是文件上传、SSRF、XXE等其他类型的Sink。

0x04 JAVA与PHP反序列化对比

相信大多数小伙伴都是从PHP开始接触代码,作为曾经世界上最好的语言,有必要来和我们当前世界上最好的语言来进行对比,看看两者反序列化过程中的差异。


1) 反序列化的数据存储格式


JAVA序列化之后的数据是满足特定序列化协议的字符流,PHP序列化之后是一种类似json的格式。JAVA序列化之后的数据是不可读的,PHP序列化之后的数据是可读的。如图4.1所示。

图4.1 PHP和JAVA序列化数据格式对比


不同语言对于序列化有不同的实现方式,同一种语言也有多种不同的序列化方式。相对于JAVA而言,PHP的序列化可读性更强,序列化之后的数据可以按照字段含义进行直接修改。


2) 反序列化触发点不同


从上面的关于JAVA反序列化的分析中可以看出,JAVA反序列化的入口点Source是readObject方法。而PHP与JAVA不同,PHP反序列化的入口点Source是__wakeup和__destruct方法。


说到PHP反序列化的触发点,就不得不提PHP的魔术方法。PHP的魔术方法是指以(__)开头的函数方法,通常是在某种条件下自动触发执行的方法。在反序列化的过程中常用的魔术方法如表4.1所示。表4.1 PHP反序列化过程中常用魔术方法

方法名

方法介绍

__wakeup

在调用unsearialize()时,自动执行该方法。属于反序列化Source之一。

__destruct

析构函数,在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。属于反序列化Source之一。

__toString

一个类被当成字符串时自动调用该方法。属于反序列化利用链中常见魔术方法之一。

__call

在对象中调用不可访问的方法时触发,即当调用对象中不存在的方法会自动调用该方法。属于反序列化利用链中常见魔术方法之一。

__set

给不可访问的属性赋值时自动调用该方法。属于特定场景下反序列化利用链会用到的方法。

__get

读取不可访问属性的值时自动调用该方法。属于特定场景下反序列化利用链会用到的方法。

__invoket

当尝试以调用函数的方式调用一个对象时自动调用该方法。属于特定场景下反序列化利用链会用到的方法。

因为PHP魔术方法的存在,为反序列化利用链的构造增加很多思路,可以通过特定魔术方法拓宽反序列化利用链。与PHP不同的是,JAVA并没有严格意义上的魔术方法,但是其实JAVA里面有一些类似的方法,如表4.2所示。
表4.2 JAVA中经常会被自动调用的方法

方法名

方法介绍

readObject

在反序列化的过程中会自动调用改方法,属于反序列化的入口Source。

toString

把对象当成字符串来操作是会自动调用该方法。

hashCode

返回该对象的hash值,集合类操作时会调用此方法。

equals

对象进行比较、排序、查找时可能可能调用此方法。

finalize

当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

JAVA没有魔术方法,只有一些经常会使用到的方法。JAVA在构造反序列化利用链时比PHP更加灵活,因为JDK代码大多数情况下还是JAVA编写的,可以方便的通过调试技巧来确定不同类方法之间的相互调用。


3) 类加载机制不同


在PHP中要调用某个类必须要引入,PHP引入类文件常见的是这四种方式require、require_once、inclue、include_once。在反序列化的过程中,反序列化利用链中的每一个类也都必须是显式引入的,即必须通过上面四个函数中的某一个引入对应的类文件。如果不引入,就会报错,如图4.2所示。

图4.2 PHP反序列化之前必须显式引入


与PHP的类加载机制不同,JAVA中类加载只与classpath有关,只要是在classpath中的类就能被直接调用。JAVA中并不要求在调用类之前进行显式引入,一般来说项目jar包中的类都可以任意调用。
我想这应该是JAVA出现的反序列化漏洞要远远多于PHP反序列化漏洞的原因之一。


4) 应用场景不同


PHP一般用于中小型项目,项目一般比较简单,代码较少。传统PHP项目是直接编写代码,现在的PHP项目会采用一些主流的PHP框架。但是PHP项目中很少使用其他第三方的组件(其实现在已经慢慢开始流行使用composer来加载第三方组件了)。JAVA一般用于大型项目,项目结构和功能复杂,代码较多。JAVA项目中一般会引入大量第三方jar包,而第三方jar包通常是反序列化利用链的重要组成部分。

0x05 结论

JAVA反序列化是经典的JAVA漏洞,上面的文章分析的都是JDK中最常见的序列化和反序列化方式。但是JAVA中还有其他的序列化和反序列化方式,包括:基于Externalizable接口的序列化和反序列化、基于fastjson的序列化与反序列化、基于XStream的序列化与反序列化、基于jackson的序列化与反序列化等。每一种序列化与反序列化都可能出现对应的反序列化漏洞,而这也是研究反序列化漏洞的重要组成部分。后续我们的课程也会持续更新这部分的内容,感兴趣的小伙伴可以点一个关注。

告别脚本小子系列丨JAVA安全(5)——序列化与反序列化

相关推荐

全球最大的H5网站模板库(h5页面模板下载)

当今社会,互联网迅猛发展,在网络营销中,客户往往通过企业的网站建设留下对该企业的第一印象,一个优秀的企业网站已成为企业发展的重要纽带,嗨创H5,拥有国内外一流的技术团队,潜心专研网站建设6年,是全球最...

wordpress集团公司网站模板:XSgr(wordpress建站公司)

小兽wordpress推出一款高端集团公司主题,打造高品质官网。高端是一种态度和坚持,因为我坚信贴合产品及品牌理念的高端深度定制才能最大化地呈现企业的务实严谨与产品的专业品质相比,某种程度上讲–...

私心推荐,小编酷爱的五款高逼格网站模板

建站宝盒的网站模板上千套之多,各有各的风格色彩,但是,弱水三千,小编我却只取一瓢饮,在这上千套模板之中,小编酷爱的网站模板有五套,让小编私心推荐一下吧!1、茶叶贸易公司网站模板小编对这款网站模板可是一...

「书讯」政府网站用户行为研究与应用

《政府网站用户行为研究与应用》作者:刘合翔著出版日期:2018年6月开本:16开出版社:经济管理出版社小编推荐《政府网站用户行为研究与应用》的主题是关于政府网站用户行为的特征规律及其在政府网站优...

免费服务器-搭建模板网站的操作流程(图文版)

之前发文《创业者的官网:如何搭建免费云服务器及操作面板(图文版)》,因为做了视频才发现,创业者对视频的需求,远远低于对图文解说的需求。因此,补充图文教程,不清楚的看官们,可以直接看视频版本进行细部学...

快收藏这些高逼格H5网站模板吧,不绕弯子直接下载

上面这些响应式H5网站是不是很炫酷,比起那些“在线一键生成”是不是好太多了?关键是,那些一键制作都不会开放源码给你,自定义性也很局限。不过说到底还是难看。今天笔者推荐大家一个模板网站,全都是高质量的响...

如何开发网站建设管理系统模板(如何开发网站建设管理系统模板图片)

根据用户网站需求文档设计美工图,并设计数据库结构,让网站开发人员可以更多地关注前台美工,先对照美工图,编写静态HTML页面,按网站建设管理系统模板语法,修改编写好的静态HTML页面,运行。不再需要对...

C语言的数据类型介绍(c语言的数据类型介绍是什么)

在计算机系统中,数据是放在内存中的,数字、文字、符号、图形、音频、视频等数据都是以二进制形式存储在内存中的,它们并没有本质上的区别,那么0001000该理解为数字8呢,还是图像中某个像素的颜色...

C 语言格式化输出函数中常用的格式符号

在之前介绍输入输出函数的文章中,有提到格式化输入输出函数都有包含一种特殊的符号——格式符号。那篇文章中关于格式符号也只是一笔带过,没有进行深入挖掘。本篇文章主要对输出函数(printf)中的一些常用格...

C#中的类型转换(c#数据转换类)

计算机存储的基本单位:字节我们知道一个字节(Byte)有8个比特(bit)构成,比特是存储的最小单位,表示0和1,但为什么计算机存储的基本单位是字节,而不是比特呢?假设我们要存储数字3(二进制:11...

Java8中String内存空间占用分析(电脑里下载的文件怎样删除才不会占用内存空间)

1.前言分析之前,简单回顾一下对象的内存分布。在HotSpot虚拟机中,对象在堆内存中的存储布局可以划分为三部分:对象头、实例数据和对齐填充。对象头包含两部分内容:MarkWord和类型指针。实例数据...

「每日C语言」数据类型大小和取值范围

对于c语言来说,数据类型是一个很重要的概念和知识点,它涉及到的是内存的空间,这在和硬件交互的时候是非常重要的。K&R给出了7个数据类型相关的关键字,分别是:int、long、short、uns...

【c语言学习笔记】数据类型(c语言里面的数据类型)

c语言学习笔记,欢迎大家能在评论区提出我学习错误的地方方便我进行改正~在计算机中,计算机用二进制来储存数据,在c语言中有许多的数据类型用来存储数据,当然不同的数据类型所用的内存占用也不一样,下面就来用...

关于MySQL varchar类型最大值,原来一直都理解错了

我是架构精进之路,点击上方“关注”,坚持每天为你分享技术干货,私信我回复“01”,送你一份程序员成长进阶大礼包。写在前面关于MySQLvarchar字段类型的最大值计算,也许我们一直都理解错误了,...

C语言数据类型的转换(c语言数据类型的转换方式)

类型转换在C语言程序中,经常需要对不同类型的数据进行运算,为了解决数据类型不一致的问题,需要对数据的类型进行转换。例如一个浮点数和一个整数相加,必须先将两个数转换成同一类型。C语言程序中的类型...

取消回复欢迎 发表评论: