FastJson checkAutoType安全机制研究
2020-08-19 10:33:08 Author: xz.aliyun.com(查看原文) 阅读量:252 收藏

在FastJson1.2.25以及之后的版本中,fastjson为了防止autoType这一机制带来的安全隐患,增加了一层名为checkAutoType的检测机制。

在之后的版本中,随着checkAutoType安全机制被不断绕过,fastjson也进行了一系列例如黑名单防逆向分析、扩展黑名单列表等加固。但是
checkAutoType的原理未曾有过大的变化,因此本文将以fastjson 1.2.25版本为例,介绍一下checkAutoType安全机制的原理

在调试分析fastjson的checkAutoType安全机制之前,发现网上很多fastjson漏洞的分析文章中曾经提到过一个名为autoTypeSupport的开关,且在1.2.25以及之后的版本中默认关闭。在动手调试之前,我曾一度以为autoTypeSupport开关关闭与否直接决定了fastjson是完全摒弃或是使用autotype功能的。但是实际调试中发现,这个开关仅仅是checkAutoType安全机制中的一个选项,这个开关的关闭与否,并不直接作用于fastjson是否使用autoType机制,下文案例中可以看出这个问题。

fastjson在1.2.25以及之后的版本中引入了一个checkAutoType安全机制,位于com/alibaba/fastjson/parser/ParserConfig.java文件。但并不是所有情况下fastjson都会加载这个机制进行安全监测,让我们下面来看看究竟什么情况下这个安全机制会被触发

通过调试fastjson 1.2.25代码发现,如果想触发checkAutoType安全机制,需要执行到com/alibaba/fastjson/parser/deserializer/JavaBeanDeserializer.java中下图红框处位置

首先看下在什么情况下不会使用checkAutoType安全机制

不使用checkAutoType安全机制的情况

一、json字符串中未使用@type字段

String jsonstr = "{\"s1\":\"1\"}";

显然,这里连@type字段都没有,这就是个最普通的json字符串转换为java对象的方法,因此根本进入不了上文中触发userType = config.checkAutoType(typeName, expectClass);的位置

二、Class\<T> clazz与@type相同

其次我们再看另一种情况

这里@type指定的类与parseObject(String text, Class\<T> clazz)中Class\<T> clazz)指定的类是一样的,都是AutoTypeTest.Test1

在上图中,由@type指定的typeName变量与parseObject(String text, Class\<T> clazz)中Class\<T>
clazz指定的beanInfo.typeName变量值完全一样,因此程序在这个if分支中,在这个上图红框中的if分支,程序不是break就是continue,无论如何也不会执行到上图552行的checkAutoType安全机制中

由上文两个例子可见,在1.2.25以及之后的版本中,并不是所有的情况都需要经过checkAutoType这一关卡的。

我们接下来看看如何触发checkAutoType安全机制,以及checkAutoType安全机制的原理

使用checkAutoType安全机制的情况

通过我的分析,checkAutoType安全机制中也是针对不同情况不同处理的,例如文章开头所说的autoTypeSupport开关等一些元素,这些元素总和起来一起觉得checkAutoType安全机制是如何过滤以及处理传入的等待反序列化的json字符串

总得来说,有如下几个元素共同作用影响checkAutoType选择哪种方式处理输入

1、autoTypeSupport开关值(True/False)

2、使用parseObject(String text, Class\<T> clazz)或是parseObject(String text)(这里Class\<T>
clazz参数为应与@type字段不一样的值,否则不会触发checkAutoType)

根据这两种条件,我们可以列出如下四种情况的表格

autoTypeSupport值 parseObject(String text, Class\<T> clazz)/ parseObject(String text)
情况一 False parseObject(String text)
情况二 False parseObject(String text, Class\<T> clazz)
情况三 True parseObject(String text)
情况四 True parseObject(String text, Class\<T> clazz)

我们逐一分析

一、autoTypeSupport值为False、使用parseObject(String text)

由于autoTypeSupport为False,程序进入如下分支

程序首先遍历denyList这一黑名单,并判断className与黑名单是否匹配

这里需要说明一下className变量是从哪来的

className变量是由typeName简单变换而来的,详见下图代码

而typeName即为@type字段值

接下来看下黑名单中的元素

denyList =
"bsh,com.mchange,com.sun.,java.lang.Thread,java.net.Socket,java.rmi,javax.xml,org.apache.bcel,org.apache.commons.beanutils,org.apache.commons.collections.Transformer,org.apache.commons.collections.functors,org.apache.commons.collections4.comparators,org.apache.commons.fileupload,org.apache.myfaces.context.servlet,org.apache.tomcat,org.apache.wicket.util,org.codehaus.groovy.runtime,org.hibernate,org.jboss,org.mozilla.javascript,org.python.core,org.springframework"

如果className命中黑名单,程序抛出异常

autoType is not support.

程序结束

黑名单过滤完成后,程序还会将className与白名单匹配一下

这里的acceptList即为白名单,默认情况下为空

开发者可以自行向acceptList中增加元素。方法见下图48行处,向白名单中增加了一个AutoTypeTest.Test1类

当className与白名单相匹,程序会将这个类返回,并将传入的json字符串反序列化为这个类的对象

然而程序执行完黑名单与白名单校验后,即没有匹配到黑名单,也没有匹配到白名单的话,程序最终会执行到下图代码段

程序抛出异常结束

由于在1.2.25以及之后的版本中,autoTypeSupport值默认False。所以在这种情况下,即使攻击者的payload绕过了黑名单,但如果你的payload不在白名单上,也是不能成功利用的。值得注意的是,白名单默认是空的

在这种情况下,payload想执行成功,有一种可能性:

  1. @type字段值在不在黑名单中且在白名单中

二、autoTypeSupport值为False、使用parseObject(String text, Class\<T> clazz)

注意上图,这里parseObject中的Class\<T> clazz参数是AutoTypeTest.Test.class,而@type中的是AutoTypeTest.Test1,二者不是一个类。如果是一个类,根据上文checkAutoType触发条件分析,根本不会触发checkAutoType

程序会执行到如下if分支中

值得注意的是,这个分支中是先匹配白名单,后匹配黑名单,如果@type字段指定的类在白名单中,则直接返回,不需要再经过黑名单过滤了。这一点很有意思,如果开发者因为开发失误,将存在利用的类加到了白名单里,攻击者是可以直接利用的

回归正文,由于上图这里我们没有向白名单中增加AutoTypeTest.Test.class类,程序会接下来检查传入的类是否在黑名单中

如果匹配到黑名单,则直接抛出错误

如果这里既没有匹配到白名单直接返回,也没有匹配到黑名单抛出错误终止,程序则继续向下执行

继续执行到的这个分支与情况一中的完全一致,又匹配了一遍黑名单与白名单。显而易见,这里既不会匹配到白名单,也不会匹配到黑名单

最后程序执行到下图这里

由于我们使用的是parseObject(String text, Class\<T> clazz)
这种方式,上图代码中872行处的expectClass即为Class\<T>
clazz传入的AutoTypeTest.Test.class类,而clazz变量为@type字段指定的AutoTypeTest.Test1.class类.程序通过

expectClass.isAssignableFrom(clazz)

判断@type字段指定的clazz变量与Class\<T> clazz传入的expectClass是否

是判断 Class\<T> clazz传入的expectClass对象表示的类或接口是否与指定的@type字段指定的clazz变量参数表示的类或接口相同,或者是其超类或父接口。这里AutoTypeTest.Test.class类与AutoTypeTest.Test1.class类所表示的类与接口不同,也不是超类或父类关系。因此程序抛出

Exception in thread "main" com.alibaba.fastjson.JSONException: type not match.AutoTypeTest.Test1 -> AutoTypeTest.Test

异常

在1.2.25以及之后的版本中autoTypeSupport值默认为False。在这种情况下,程序会先匹配白名单,后匹配黑名单。如果@type字段指定的类在白名单中,则程序是会跳过黑名单校验的,例如下图

即使com.sun.rowset.JdbcRowSetImpl在黑名单中,但在开发的时候,由于开发安全意识不强或开发疏忽等原因,将com.sun.rowset.JdbcRowSetImpl加入了白名单,此时是可以绕过黑名单直接执行利用的

在这种情况下,payload想执行成功,有两种可能性:

1、没有命中黑名单且Class\<T> clazz表示的类或接口是否与指定的@type字段值表示的类或接口相同,或者是其超类或父接口。

2、@type字段值在白名单中

三、autoTypeSupport值为True、使用parseObject(String text)

这里首先进入了与autoTypeSupport值为False、使用parseObject(String text, Class\<T> clazz)类型相同的分支

先匹配白名单,后匹配黑名单。如果@type字段指定的类在白名单中,则直接返回,不再进行黑名单校验。在白名单未匹配成功后,使用黑名单进行匹配,若匹配到黑名单,直接抛出异常。如果黑白名单都未匹配成功,程序继续向下执行

程序将@type字段指定的类返回

这种情况下要是payload想成功利用有两种办法:

1、@type字段值只需要不在黑名单中即可成功利用

2、@type字段值在黑名单中,但是开发的时候在白名单中加入了这个类,payload依然可以成功利用

四、autoTypeSupport值为True、使用parseObject(String text, Class\<T> clazz)

与上文二、三节相同相同,先进入了这个分支

这里不再复述了

在没有匹配到黑白名单后,程序执行到了下图这里

由于这里clazz与expectClass所表示的类与接口不同,也不是超类或父类关系。因此程序抛出

Exception in thread "main" com.alibaba.fastjson.JSONException: type not match.AutoTypeTest.Test1 -> AutoTypeTest.Test

异常

在这种情况下,payload想执行成功,有两种可能性:

1、没有命中黑名单且Class\<T>
clazz表示的类或接口是否与指定的@type字段值表示的类或接口相同,或者是其超类或父接口。

2、@type字段值在白名单中

早期checkAutoType安全机制缺陷

在fastjson 1.2.25版本引入的checkAutoType以及后续的几个版本中存在着一定的缺陷

如上文所分析,程序通常先经过黑名单与白名单的校验后,将满足条件的clazz进行返回

我们来看下这个clazz是如何获得的

可见clazz是通过@type字段值获得而来

解析来看下TypeUtils.loadClass是如何实现的

注意上图的1089行

当传入的className变量以”L”开头,并以”;”结尾,进入上图分支

程序将会把开头的”L”与结尾的”;”去掉,并递归调用loadClass加载这个类

因此可以下图这样构造来进行绕过

loadClass会将”L”与”;”去除后组成newClassName并返回

这一操作在匹配黑白名单之后,Lcom.sun.rowset.JdbcRowSetImpl;恰好可以绕过黑名单中的限制。后续checkAutoType检测机制进行了一系列的安全加固,大体上都是黑名单防逆向分析、扩展黑名单列表等,但checkAutoType检测机制没有太大的改变。受篇幅影响,这里就不再详细分析了。


文章来源: http://xz.aliyun.com/t/8140
如有侵权请联系:admin#unsafe.sh