Java-Jackson反序列化漏洞及挖洞思路
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
源码分析Jackson反序列化漏洞
前言:
本次分析从Java序列化和反序列化源码开始分析,进一步分析Jackson源码,找出造成漏洞的原因,最后以Jackson2.9.2版本,JDK1.80_171,resin4.0.52,CVE-2020-10673为例复现漏洞。
一.JA V A反序列化原理
1.1 Class对象
每一个类都有一个Class对象,Class对象包含每一个类的运行时信息,每一个类都有一个Class对象,每编译一个类就产生一个Class对象,Class类没有公共的构造方法,Class对象是在类加载的时候由JVM以及通过调用类加载器中的DefineClass()方法自动构造的,因此不能显式地声明一个Class对象。在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载。如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件。一旦某个类的Class对象被载入内存,我们就可以它来创建这个类的所有对象以及获得这个类的运行时信息。
获得Class对象的方法:
1).Class.forName(“类的全名”);//com.xx.xx.xx
2).实例对象.getClass()
3).类名.class
1.2反射
JA V A反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
实现Java反射机制的类都位于ng.reflect包中:
1).Class类:代表一个类
2).Field类:代表类的成员变量(类的属性)
3).Method类:代表类的方法
4).Constructor类:代表类的构造方法
5).Array类:提供了动态创建数组,以及访问数组的元素的静态方法
简单反射调用代码
Class clz=this.getClass();
Object obj= clz.getMethod("方法名",Class对象序列).invoke(this,Object参数序列);
1.3 反序列化
Java 序列化是指把Java 对象转换为字节序列的过程便于保存在内存、文件、数据库中,ObjectOutputStream类的writeObject() 方法可以实现序列化。
Java 反序列化是指把字节序列恢复为Java 对象的过程,ObjectInputStream 类的readObject() 方法用于反序列化。
RMI:是Java 的一组拥护开发分布式应用程序的API,实现了不同操作系统之间程序的方法调用。值得注意的是,RMI 的传输100% 基于反序列化,Java RMI 的默认端口是1099 端口。
JMX:JMX 是一套标准的代理和服务,用户可以在任何Java 应用程序中使用这些代理和服务实现管理,中间件软件WebLogic 的管理页面就是基于JMX 开发的,而JBoss 则整个系统都基于JMX 构架。
只有实现了Serializable接口的类的对象才可以被序列化,Serializable 接口是启用其序列化功能的接口,实现java.io.Serializable 接口的类才是可序列化的,没有实现此接口的类将不能使它们的任一状态被序列化或逆序列化。
readObject() 方法的作用正是从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回,readObject() 是可以重写的,可以定制反序列化的一些行为。
readObject()主要做的事情,其实就是读取正常应该被序列化的字段信息后,
再构造出一个map,再通过对象流,将原有通过对象流写进文件里面的map信息(容量,每个item信息等)全部读取出来,然后重新构造一个map,这样就使得我们保存在set里面的信息,在经历过对象流的序列化和反序列化后,都没有丢失。
1.4 readObject()分析
1).进入readObject()源码,首先判断是否调用readObjectOverride(),其中enableOverride的值由ObjectInputStream类的构造函数决定,带参构造为false,无参构造为true。但readObjectOverride()函数为空方法。因此一般需要带参构造。
2).调用readObject0(),进入函数中,发现有一大部分的switch判断,该部分判断读入的流是什么类型。其中readOrdinaryObject()值得注意,因为读入的大部分都是要反序列化的对象数据流。
1.5 readOrdinaryObject()函数中readClassDesc()分析
1.进入readOrdinaryObject()函数,发现一开始,调用readClassDesc(),进而调用checkDeserialize()。如下图:
2.readClassDesc()函数中,switch判断顶端字节特征,选择执行的函数
3.无参构造创建ObjectStreamClass()对象,调用initNonProxy().
4.过程中调用了lookup(Class,boolean)方法
5.进入lookup方法,生成一个带参构造的ObjectStreamClass对象,这个对象就是下面要提到的readOrdinaryObject()函数中的readSerialData()的第一个参数。
1.6 readOrdinaryObject()函数中readSerialData()分析
1.进入readSerialData(),注意obj参数
2.进入invokeReadObject(),发现obj参数(该参数上文提到)直接经过反射调用了里面的方法