七叶笔记 » java编程 » java安全fastjson1.2.24反序列化TemplatesImpl分析

java安全fastjson1.2.24反序列化TemplatesImpl分析

前言

漏洞环境:

fastjson1.2.24

jdk1.7.80

新建一个maven项目在pom.xml文件中引入fastjson的依赖:

fastjson是alibaba开源的一个用于处理json数据格式的解析库,它支持将java对象解析成json字符串格式的数据,也可以将json字符串还原成java对象。不难看出,java对象转换成json数据就是序列化操作,而将json数据还原成java对象就是反序列化过程。

1. fastjson序列化

现在我们来看一下fastjson序列化过程,定义一个pojo类:

示例程序:

执行结果如下:

 method: Student(String name , int age)

 method: getAge()

 method: getName()

{"age":3,"name":"zhangsan"}

fastjson调用toJSONString方法将Student对象转换成json字符串数据的过程中会调用对象的getter方法。

另外toJSONString方法在进行序列化时还可以指定一个可选的SerializerFeature.WriteClassName参数,指定了该参数后,在序列化时json数据中会写入一个@type选项,

如下所示:

 json数据中的@type选项用于指定反序列化的类,也就是说所,当这段json数据被反序列化时,会按照@type选项中指定的类全名反序列化成java对象。

2. fastjson反序列化

fastjson提供了两个反序列化函数:parseObject和parse,我们通过示例程序来看一下fastjson的反序列化过程

方式一调用了parseObject方法将json数据反序列化成java对象,并且在反序列化过程中会调用对象的setter和getter方法。

方式二调用了parseObject方法进行反序列化,并且指定了反序列化对象Student类,parseObject方法会将json数据反序列化成Student对象,并且在反序列化过程中调用了Student对象的setter方法。

方式三调用了parse方法将json数据反序列化成java对象,并且在反序列化时调用了对象的setter方法。

关于Feature.SupportNonPublicField参数:

以上这三种方式在进行反序列化时都会调用对象的构造方法创建对象,并且还会调用对象的setter方法,如果私有属性没有提供setter方法时,那么还会正确被反序列化成功吗?为了验证这个猜想,现在我们把Student对象的私有属性name的setter方法去掉。

从程序执行结果来看,私有属性name并没有被正确反序列化,也就是说fastjson默认情况下不会对私有属性进行反序列化。

如果需要将私有属性反序列化时,就可以调用parseObject方法指定Feature.SupportNonPublicField参数,

如下所示:

 方式一反序列化时没有指定Feature.SupportNonPublicField参数,私有属性name没有反序列化时没有值,方式二和方式三指定了Feature.SupportNonPublicField参数后,私有属性name可以正确被反序列化。

3. fastjson反序列化漏洞原理

在上一小节中我们知道fastjson在进行反序列化时会调用目标对象的构造,setter,getter等方法,如果这些方法内部进行了一些危险的操作时,那么fastjson在进行反序列化时就有可能会触发漏洞。

我们通过一个简单的案例来说明fastjson反序列化漏洞原理

在这个示例程序中先是构造了一个恶意类,然后调用toJSONString方法序列化对象写入@type,将@type指定为一个恶意的类TestTempletaHello的类全名,当调用parse方法对TestTempletaHello类进行反序列化时,会调用恶意类的构造方法创建实例对象,因此恶意类TestTempletaHello中的静态代码块中就会被执行。

执行结果如下:

4. fastjson1.2.24漏洞复现

在实际场景中很多类没有这么明显的可以产生漏洞的代码,往往需要攻击者自己想方设法通过一些操作(例如反射,类加载,一些危险的函数)来构造一个漏洞利用环境。

在学习CC2利用链的时候我们分析了一个基于TemplatesImpl类的利用链,该类会把_bytecodes属性的字节码内容加载并实例化,关于TemplatesImpl类的利用链的具体介绍可参考CC2利用链。

fastjson1.2.24的反序列化漏洞也是基于TemplatesImpl类来构造利用链,思路如下:

1. 构造一个恶意类TempletaPoc继承AbstractTranslet类,通过javassist字节码编程将恶意类TempletaPoc转换成字节码并进行base64编码。2. 然后构造TemplatesImpl类的json数据,将TempletaPoc类的字节码设置到_bytecodes属性中,当json数据在还原成TemplatesImpl对象时会加载_bytecodes属性中的TempletaPoc类并实例化,从而触发漏洞。

定义一个恶意类TempletaPoc继承AbstractTranslet类,通过javassist将恶意类TempletaPoc转换成字节码,然后进行base64编码

最终的poc代码:

这里解释一下payload的构造:

@type:当fastjson根据json数据对TemplatesImpl类进行反序列化时,会调用TemplatesImpl类的getOutputProperties方法触发利用链加载_bytecodes属性中的TempletaPoc类字节码并实例化,执行RCE代码。_bytecodes:前面已经介绍过了,主要是承载恶意类TempletaPoc的字节码。_name:关于_name属性,在调用TemplatesImpl利用链的过程中,会对_name进行不为null的校验,因此_name的值不能为null(具体可参考CC2利用链)_tfactory:在调用TemplatesImpl利用链时,defineTransletClasses方法内部会通过_tfactory属性调用一个getExternalExtensionsMap方法,如果_tfactory属性为null则会抛出异常,无法根据_bytecodes属性的内容加载并实例化恶意类outputProperties:json数据在反序列化时会调用TemplatesImpl类的getOutputProperties方法触发利用链,可以理解为outputProperties属性的作用就是为了调用getOutputProperties方法。

漏洞利用成功,接下来我们分析一下parseObject方法是如何触发漏洞的

5. fastjson1.2.24漏洞分析

参数features是一个可变参数,parseObject方法底层实际上是调用了parse方法进行反序列化,并且将反序列化的Object对象转成了JSONObject

parse方法会循环获取可变参数features中的值,然后继续调用parse方法

分析parse方法:

parse方法创建了一个JSONObject对象存放解析后的json数据,而parseObject方法作用就是把json数据的内容反序列化并放到JSONObject对象中,JSONObject对象内部实际上是用了一个HashMap来存储json。

继续跟进parseObject方法;

parseObject方法主要是从json数据中提取@type并进行校验是否开启了autoType功能,接着会调用loadClass方法加载@type指定的TemplatesImpl类,然后将TemplatesImpl类的class对象封装到ObjectDeserializer 中,然后调用deserialze方法进行反序列化。

我们来看一下deserializer的内容,如下图所示:

 TemplatesImpl类的每个成员属性封装到deserializer的fieldInfo中了。

然后调用了deserialze方法,该方法中的参数如下所示:

deserialze方法内部的代码逻辑实在是太复杂了,内部有大量的校验和if判断,这里只是简单的分析了大概的逻辑,这些已经足够我们理解TemplatesImpl的利用链了,后期深入分析fastjson的防御机制,以及在构造payload如何绕过校验机制时,再深入分析deserialze方法分析fastjson的解析过程做了哪些事情,目前我们先把TemplatesImpl的利用链搞清楚再说。

 我们只分析deserialze方法中的部分核心代码,deserialze方法内部主要是调用了createInstance方法返回一个object类型的对象(也就是TemplatesImpl对象),然后调用了parseField方法解析属性字段。

parseField方法:

parseField方法内部会对参数features中的SupportNonPublicField选项进行校验,这个if判断主要是获取TemplatesImpl对象的所有非final或static的属性,如果fastjson调用parseObject方法时没有设置SupportNonPublicField选项的话,就不会进入这个if判断,那么fastjson在进行反序列化时就不会触发漏洞。

校验完SupportNonPublicField选项后,调用parseField方法解析TemplatesImpl对象的属性字段,先来看一下parseField方法的参数

parseField方法主要会做以下事情,调用fieldValueDeserilizer的deserialze方法将json数据中每个属性的值都提取出来放到value 中,然后调用setValue方法将value的值设置给object。

可以看到deserialze方法将json数据中的_bytecodes值提取出来进行base64解码存放到value中,接着调用setValue方法将value设置给object(即TemplatesImpl对象的_bytecodes)。

继续跟进fieldValueDeserilizer的deserialze方法

deserialze方法内部调用了bytesValue方法。

bytesValue方法内部调用了确实对json数据中的_bytecodes值进行了base64解码

前面我们说过json数据中的outputProperties的作用是触发TemplatesImpl利用链的

触发漏洞的关键就在于当fastjson调用setValue方法将json数据中的outputProperties的值设置给TemplatesImpl对象时会触发漏洞,调用TemplatesImpl类的getOutputProperties方法。

继续分析setValue方法是如何触发漏洞的

setValue方法对value进行了不为null的校验,然后解析_outputProperties(json中的_outputProperties被封装到了fieldInfo中)。

fastjson会将属性的相关信息封装到fieldInfo中,具体信息如下:

然后判断method中的getOutputProperties的返回值是否为Map,为什么是通过Map接口的class对象来判断?因为Properties实现了Map接口,因此这个判断满足条件会通过反射调用TemplatesImpl对象的getOutputProperties方法。

关于getOutputProperties方法的调用

不知道大家有没有思考过fastjson是如何获取到getOutputProperties方法的?原因在于parseField方法内部调用了JavaBeanDeserializer类的smartMatch方法

smartMatch方法会将json中的_outputProperties中的下划线去掉,替换成outputProperties并封装到fieldInfo中,我们知道fastjson在反序列化过程中会调用属性的getter方法,因此这里还会将outputProperties属性的getter方法也封装到fieldInfo中的method当中。

我们貌似... 大概知道了getOutputProperties方法是如何获取的,继续思考一下:fastjson在反序列化过程中具体是如何调用属性的getter方法的?

答案是JavaBeanInfo类中有一个build方法,当通过@type获取TemplatesImpl类的calss对象后,会通过反射获取该类的class对象的所有方法并封装到Method数组中。然后通过for循环遍历Method获取getter方法,并将outputProperties属性和getter方法(getOutputProperties方法)一起封装到FieldInfo,从代码中确实可以看到add方法会将FieldInfo放到了一个fieldList中,然后将fieldList封装到JavaBeanInfo

getter方法的查找方式需要满足以下几个条件:

方法名长度不小于4

必须是非静态方法

必须get字符串开头,并且第四个字符为大写字母

方法中不能有参数

返回值继承自Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong

在getter方法中不能有setter方法

这样一来自然就获取到了getOutputProperties( )方法,当setValue方法从FieldInfo获取到outputProperties属性和getOutputProperties方法并反射调用getOutputProperties方法就会触发TemplatesImpl利用链。

getOutputProperties方法内部调用了newTransformer方法

newTransformer方法内部调用了getTransletInstance方法

getTransletInstance方法内部会对_name和_class进行不为null校验, 我们构造的payload没有_class,因此这里_class为null会调用defineTransletClasses方法加载_bytecodes属性的类字节码(加载TempletaPoc类)。

跟进defineTransletClasses方法:

defineTransletClasses方法内部会加载_bytecodes中的类字节码数据(加载TempletaPoc类),并且会校验TempletaPoc类是否继承了AbstractTranslet类。然后返回到getTransletInstance方法中,调用newInstance方法实例化TempletaPoc类执行RCE代码。

到此这篇关于java安全fastjson1.2.24反序列化TemplatesImpl分析的文章就介绍到这了,更多相关13-javafastjson1.2.24反序列化内容请搜索七叶笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持七叶笔记!

相关文章