天天看点

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

一、前言

很久以前写过一篇文章《反序列化漏洞攻击原理(Dubbo反序列化漏洞剖析)》,最近在看评论的时候无意中发现我当时在文中留的一个小思考有朋友在询问答案。原始问题是:在反序列化攻击中为什么我们要大费周章执行恶意代码呢?为什么我们不自己在本地定义一个实例,让其在readObject的时候直接执行恶意代码。然后将这个实例序列化之后发送到服务端反序列化即可?

今天我们就来一起深入分析下这个问题。

二、代码验证

接下来我们直接通过代码来看看,如果我们自己构建一个服务端不存在的类实例发送到服务进行反序列化操作会有什么现象。

我们的思路为:将一个类实例序列化并写入到文件中。然后将这个文件数据读取出来直接序列化为对象。以此来模拟服务端被反序列化攻击的场景。

1、公共代码

序列化方法

将对象序列化之后,并将序列化数据写入到文件中。

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

 反序列化方法

从文件中读取出序列化数据,并进行反序列化。

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

  被序列化对象

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

2、正常序列/反序列化

我们先看看正常序列化和反序列化一个对象的现象。通过如下代码我们先将Person对象p序列化到文件中,然后再从文件中将其数据拿出来反序列化为p2对象。然后对比序列化前和反序列化后两个对象是否相等。

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

 通过如下结果可知,序列化对象p和反序列化对象p2的内部成员变量完全相同。因此这两个对象是“相等”的。当然这也是常规序列化和反序列化应该有的表现。

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

3、异常反序列化模拟

这里我将模拟测试将一个服务端不存在的类实例序列化数据发送到服务端进行反序列化后会有什么现象。

首先我们将Person的对象序列化到文件中(通过下图中注释掉的代码)。然后将Person类从代码中删除,接着通过unSerial方法将Person类的序列化数据读取出来进行反序列化为对象(下面代码中p2被定义为Object是因为Person类已经被删除了)。

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

执行上述红框中的代码后,我们并没有能够得到p2对象,而是获得了如下错误:ClassNotFoundException。 

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

所以通过代码模拟实验我们得出如下结论:如果将一个服务端不存在的类实例的序列化数据发送到服务端进行反序列化操作不能成功,而会得到一个ClassNotFoundException错误。

三、根因分析

通过上述代码模拟实验我们已经知道我们反序列化的类实例必须在服务端存在。那么这是为什么呢?

我们知道在对象的实例化之前,我们需要先从类加载器中找到其对应的Class类。而由于我们的Person类不存在,所以也就没有Class类,因此反序列化的时候就会报错。

那么服务端在反序列化的时候是怎么知道需要哪个类的呢?我们打开序列化文件中的内容(如下图)可以看到,在序列化数据中其存储了该序列化数据的类信息,以及成员变量基本信息。Java反序列化的时候就是根据这些信息将对应的类实例构建出来的。

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

四、惯例

如果你喜欢本文或觉得本文对你有所帮助,欢迎一键三连支持,非常感谢。

如果你对本文有任何疑问或者高见,欢迎添加公众号lifeofcoder共同交流探讨(添加公众号可以获得楼主最新博文推送以及”Java高级架构“上10G视频和图文资料哦)。

给服务端发送自定义类实例序列化数据实现反序列化攻击?一、前言二、代码验证三、根因分析四、惯例

继续阅读