# Java反序列化协议解析 一

**URL:** <https://forum.90sec.com/t/topic/1523>\
**Category:** 宽字节安全\
**Created:** [2021 年1 月 13 日 08:45 UTC](https://forum.90sec.com/t/topic/1523 "2021-01-13T08:45:29Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

作者： ![unicodeSec](https://forum.90sec.com/user_avatar/forum.90sec.com/unicodesec/32/6830_2.png) [@unicodeSec](https://forum.90sec.com/u/unicodeSec)\
发布日期： [2021 年1 月 13 日 08:45 UTC](https://forum.90sec.com/t/topic/1523/1 "2021-01-13T08:45:29Z")

</div>

年前开始研究Java反序列化，一直没有研究Java反序列化文件的格式。最近闲来无事，研究一下java反序列化文件的格式。扯这么多的原因主要是要达成两个目标

1. 尝试使用python读取java反序列化文件，并转换为Json格式以方便阅读（其实没多少用，还是一样难懂）。
2. json定义反序列化，随意编辑反序列化流，通过json生成序列化文件。缩小某些payload工具的大小，生成一个exp需要N多依赖的jar包，这不讲伍德

首先我们先大致讲解一下java序列化以及相关东西。

1. java序列化功能只传递对象的属性，不传递对象的方法，静态变量等等。你可以把java序列化功能类比Json传输。
2. java存在八种基本类型，分别是字节，整型数字，浮点数，布尔值。至于对象，其实是这八种基本类型的复杂组合。无论多么复杂的对象，其实都可以被解析为这八种基本类型。当然这八种基本类型在其他语言中也存在，为我们使用python解析反序列化文件提供了理论基础。

## 1 基础类型的读取

序列化中也有基础类型。我们需要定义几个函数用来读取基础类型。  
例如

- 一个Byte一般作为指令，标记后面的数据流是什么
- int为4个Byte，也就是32位byte
- Short为2个Byte，是16位
- 以此类推，按照c语言的数据类型长度定义，千万别乱写
- 字符串类型，在这里我们只讨论ascii编码的情况，暂时不讨论Utf编码的情况。在序列化中，一个字符串类型，首先读取两个Byte，也就是short类型，作为字符串的长度。随后按照字符串的长度，读取Byte作为字符串。

## 2 序列化中控制指令

前面我们提到，读取一个Byte作为控制指令，控制指令的作用是标记后面的数据类型，序列化中控制指令如下

- `TC_NULL = b'\x70' ` 标记后面的数据为空，对应java就是Null
- `TC_REFERENCE = b'\x71` java序列化协议是一个格式十分紧凑的协议，是不会出现两个一摸一样的对象，类等。如果第二次出现，则会通过reference去指向之前的那个内容。你可以把这个类比为指针。
- `TC_CLASSDESC = b'\x72'` 这个是处理并返回类描述符。。与下面的class的区别在于，这个返回的是 **描述类的一个对象** ，主要包括类的名称，suid等各种属性。
- ` TC_CLASS = b'\x76'`在java序列化中，类的传输通过名称，suid等属性，对端通过名称查找classpath中该类。而这个TC\_CLASS将会根据上面的类描述符，通过Class.forName去查找这个类。
- `TC_OBJECT = b'\x73'` 标记后面的数据为Object对象
- ` TC_STRING = b'\x74'`标记后面的数据字符串。与基本类型中字符串的区别在于，这里面读取的字符串将会被缓存，如果出现第二个一模一样的字符串，则通过reference的方式，直接读取缓存中的字符串
- ` TC_ARRAY = b'\x75'`标记后面的数据为数组类型
- ` TC_BLOCKDATA = b'\x77'` 在对象的WriteObject方法中，我们可以自定义的写入数据，除了非Object数据，其他所有数据将会被写在一起，也就是BlockData。当然，只有readObject方法中，合适的读取顺序才可以成功还原blockdata。
- ` TC_ENDBLOCKDATA = b'\x78'` 在readObject中，表明数据已经读取完毕
- ` TC_EXCEPTION = b'\x7B'` 表明后面需要读取一个exception类型的对象
- ` TC_PROXYCLASSDESC = b'\x7D'` 读取一个动态代理的对象

## 3 还原反序列化流

有了上面的知识作为基础，下面我们尝试还原反序列化流中的内容。当然我们并没有按照某些特定的顺序去讲解。

### 3.1 还原类的描述符（ClassDesc

类的描述符为序列化协议中的基石，它表明了后面的数据类型以及读取方法。类的描述符，一共有以下几个字段

- name 字符串类型，类的名称
- suid long类型，类的suid，为了防止兼容性而设置的一个值。同一类的不同版本可能suid不一直，这样防止不兼容的情况发生
- flag 表明类是否为反序列化，是否存在writeObject方法，也就是额外写入数据等等
- field 类中包含的数据类型列表，
- 父类，父类也是类的描述符
- 类的额外信息

#### field

这里只包含类的数据类型的名称与类型，不包括值。一定注意

#### 读取父类

由于java不支持多继承，所以在这里只有两种情况，继承自一个父类和没有父类这两种情况。在这里我们只需要递归读取，直到控制指令为TC\_NULL，即父类为空，作为结束递归的条件。

> 类的描述符，记得要缓存。计算handle的时候，后面的值可能会引用该类的描述符

下面用一图总结类的描述符的的协议结构

```auto
| utf 类名|4Byte suid|1Byte flags|2Byte 字段数量|字段详细内容|父类|类的额外数据|

```

### 3.2 还原数组（TC\_ARRAY

我们知道，java的数组中的内容只能为同一类型。所以在处理数组信息的时候，首先读取类描述符，表明数组中的内容的数据类型。然后读取数组的长度。最后按照数组长度以及数组类型，去读取并还原数组中的数据。

```auto
|ClassDesc|Int length|数组数据|

```

> 数组也会被缓存，并被计算为handle

### 3.3 还原对象

还原对象的数据其实特别简单。首先读取类的描述符，然后紧接着按照类描述符的字段读取数据即可。所以在这里，一个byte出错，将会导致后面的数据全部读取错误。  
在这里需要注意几点

1. 首先读取父类中字段的值，然后再读子类的字段值，在这里我们使用栈数据结构去解决读取
2. 如果父类包含额外信息（例如writeObject写入），则首先读取父类包含的额外信息，再去读取子类的额外信息
3. 如果对象继承自EXTERNALIZABLE接口，则无法单纯通过流中数据还原对象中的值。因为java将不会负责字段值的读取写入，这一切都由开发人员决定哪些字段被保存以及保存的方法。这也就是weblogic中反序列化触发XXE的漏洞原理，出现问题的类基本都继承自EXTERNALIZABLE接口，且通过xml定义被保存的对象
4. 如果对象继承自Serializable接口，且存在writeObject方法。当writeObject方法中没有通过ObjectOutputStream.defaultWriteObject将类的默认字段写入到序列化流中，也无法还原对象的值。原因在于，反序列化中读取类的值按照类的字段顺序去读取，如果没有调用defaultWriteObject写入，则相当于顺序不可知，也是无法单纯通过流中数据去还原对象

当然，对象中字段的值有可能还是对象，需要递归读取，直到读取所有的字段为基础数据类型。在这里建议设置递归的最大深度，防止出现爆栈的异常。

## 4. 总结

目前完成各种复杂对象的读取，例如x友的exp读取，weblogic 反序列化Exp的读取，并转换为json。不过代码还未写完，地址暂时为[https://github.com/potats0/javaSerializationDump/blob/main/main.py](https://github.com/potats0/javaSerializationDump/blob/main/main.py)

截图如下，读取x友exp

![](https://potatso-1253210846.cos.ap-beijing.myqcloud.com//img20201228174340.png)  
 ![](https://potatso-1253210846.cos.ap-beijing.myqcloud.com//img20201228174516.png)

x友exp转json的结果

> <https://gist.github.com/potats0/108fe530350d14b11aba79736d6a3f0c#file-ncexp-json>

下篇文章将谈一下如何将json转为序列化文件以及相关数据结构的设计。

![](https://potatso-1253210846.cos.ap-beijing.myqcloud.com//imgWeChat%20Image_20200612150038.png)
