zoukankan      html  css  js  c++  java
  • Java反序列化协议解析 一

    年前开始研究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的时候,后面的值可能会引用该类的描述符

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

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

    3.2 还原数组(TC_ARRAY

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

    |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

    截图如下,读取x友exp


    x友exp转json的结果
    https://gist.github.com/potats0/108fe530350d14b11aba79736d6a3f0c#file-ncexp-json

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

  • 相关阅读:
    【HTML5 绘图与动画】使用canvas
    【H5新增元素和文档结构】新的全局属性 1. contentEditable 可编辑内容 2. contextmenu 快捷菜单 3. data 自定义属性 4. draggable 可拖动 5. dropzone 拖动数据 6. hidden 隐藏 7. spellcheck 语法检查 8. translate 可翻译
    【H5新增元素和文档结构】完善旧元素 1. a 超链接 2. ol 有序列表 3. dl 定义列表 4. cite 引用文本 5. small 小号字体 6. iframe 浮动框架 7. script 脚本
    【H5新增元素和文档结构】新的语义信息 1. address 2. time 3. figure 跟 figcaption 4. details 和 summary 5. mark 6. progress 7. meter 8. dialog 9.bdi 10. wbr 11. ruby、rt、rp 12. command
    【H5新增元素跟文档结构】新的文档结构 1. article 文章块 2. section 区块 3. nav 导航条 4. aside 辅助栏 5. main 主要区域 6. header 标题栏 7. hgroup 标题组 8. footer 页脚栏
    5_PHP数组_3_数组处理函数及其应用_9_数组集合运算函数
    【华为云技术分享】鲲鹏弹性云服务器GCC交叉编译环境搭建指南
    【华为云技术分享】7 分钟全面了解位运算
    【华为云技术分享】Linux内核编程环境 (1)
    【华为云技术分享】华为云MySQL 8.0正式商用,全新增强版开源利器强势来袭
  • 原文地址:https://www.cnblogs.com/potatsoSec/p/14202792.html
Copyright © 2011-2022 走看看