前言
Fastjson 1.2.24 版本的远程代码执行漏洞可谓是开辟了近几年来Fastjson漏洞的纪元。在Fastjson 1.2.24版本反序列化漏洞初次披露之后,官方针对这个漏洞进行了修补。然而这个修复方案很快就被发现存在绕过的风险。由于官方的修复方案或多或少都存在着一些问题,随之而来的是一次又一次的绕过与修复。
回顾一下Fastjson反序列化漏洞,简单来说就是:fastjson通过parse、parseObject处理以json结构传入的类的字符串形时,会默认调用该类的共有setter与构造函数,并在合适的触发条件下调用该类的getter方法。当传入的类中setter、getter方法中存在利用点时,攻击者就可以通过传入可控的类的成员变量进行攻击利用。com.sun.rowset.JdbcRowSetImpl这条利用链用到的是类中setter方法的缺陷,而TemplatesImpl利用链则用到的是getter方法缺陷。
官方主要的修复方案是引入了checkAutotype安全机制,通过黑白名单机制进行防御。在随后的版本中,为了增强漏洞绕过的难度,又在checkAutotype中采用了一定的加密混淆将本来明文存储的黑名单进行加密。
checkAutotype安全机制
Fastjson从1.2.25开始引入了checkAutotype安全机制,通过黑名单+白名单机制来防御。
我们先来看一下1.2.25版本初次引入的checkAutotype安全机制的形式
首先看一下如下图样例
这是一个很普通的测试样例,用来将字符串转变为Java对象。
执行结果如下
这个测试样例在1.2.24版本是可以执行成功的,但在1.2.25版本中却爆出来autoType is not support错误。
这是因为Fastjson 1.2.25版本引入了checkAutotype安全机制。在默认情况下AutoTypeSupport关闭且测试样例中的AutoTypeTest.ForTest类虽然不在黑名单上,但也并不在checkAutotype安全机制白名单上,因此使用上图测试样例在Fastjson 1.2.25版本中反序列化失败。
将AutoTypeSupport设置为打开时,见下图
开启AutoTypeSupport后,AutoTypeTest.ForTest类反序列化成功
接下来修改测试样例,换成恶意类,再进行一次实验
经过动态调试可以发现,程序在执行到com/alibaba/fastjson/parser/ParserConfig.java中checkAutoType安全机制时,com.sun.rowset.JdbcRowSetImpl类会触发黑名单,程序将抛出错误
看一下Fastjson 1.2.25版本中引入的黑名单中元素都有哪些,见下图
这次实验中的poc无法成功利用,是因为com.sun.rowset.JdbcRowSetImpl类命中了黑名单而不能反序列化成功
在这里额外介绍一下:当程序经过黑白名单的校验之后,接下来会通过TypeUtils.loadClass方法对类进行反序列化加载
1.2.41版本漏洞
1.2.41版本漏洞利用,其实就是针对1.2.24版本漏洞所打的补丁的绕过,本次漏洞影响了1.2.41版本以及之前的版本
首先来看一下1.2.41版本漏洞利用的poc
可以发现,@type字段值为”Lcom.sun.rowset.JdbcRowSetImpl;”
在”com.sun.rowset.JdbcRowSetImpl”类的首尾多出了一个L与;
经过上文的介绍,我们知道@type字段值会经过黑名单校验
黑名单的检测机制很简单,就是用黑名单中的元素依次与@type字段值进行字符串匹配,从@type字段值从首位开始,匹配到了黑名单中的元素,就会抛出错误。
很明显,”Lcom.sun.rowset.JdbcRowSetImpl;”并不会匹配到黑名单。但是有一个问题:黑名单匹配机制明显不管@type字段值末位是什么,又是为何在末位加一个”;”呢?首位的”L”又是什么呢?我们随后会介绍一下这个。
通过上文对checkAutotype安全机制的解释可以发现,@type字段值首先会经过黑白名单的校验。在成功通过校验之后,程序接下来会通过TypeUtils.loadClass方法对类进行加载
跟入位于com/alibaba/fastjson/util/TypeUtils.java中的loadClass方法
从上图可见,在该方法中有两次if语句,见上图红框处
从字面意义上来看,第一处的作用是匹配以”[”开头的字符串,而第二处则是匹配以”L”开头,以”;”结尾的字符串。
这一点看起来与poc中的结构很相似,poc中构造的结构应该就是为这里准备的。
经过我们上文的分析,已经对漏洞以及构造有了一定的了解。接下来重点来看下图FastJson解析类描述符的代码
当传入的类名以”L”开头,且以”;”结尾时,程序将去除首尾后返回。
这意味着,如果在恶意类前后加上”L”与”;”,例如”LAutoTypeTest.ForTest;”,这样不仅可以轻易的躲避黑名单,随后在程序执行到这里时,还会将首尾附加的”L”与”;”剥去。剥皮处理之后字符串变成”AutoTypeTest.ForTest”,接着”AutoTypeTest.ForTest”被loadClass加载,恶意类被成功反序列化利用。
我们测验以下是否有效
动态调试后发现,程序的确进入如下if分支,并且剥去前后”L”与”;”
1.2.42版本漏洞
Fastjson 1.2.42版本在处理了1.2.41版本的漏洞后。很快又被发现存在着绕过方式。
分析下Fastjson1.2.42中的checkAutotype安全机制,看看是怎么处理的1.2.41版本漏洞。见下图
从上图可以发现,不同于之前的版本,程序并不是直接通过明文的方式来匹配黑白名单,而是采用了一定的加密混淆。此时的黑名单也变成如下的样子
除了引入了黑名单加密混淆机制外,checkAutotype中也加入了一些新的机制,如下图这里
与之前的版本相比,这里多出了上图这一段代码。从代码上大体可以猜测出来,这是用来判断类名的第一个字符与最后一个字符是否满足一定条件,然后将首尾剥去
又看到熟悉的剥皮操作了,我们不难猜测到这两个满足条件的字符大概率是”L”与”;”。开发者的用意大概是想针对于1.2.41版本的利用”Lcom.sun.rowset.JdbcRowSetImpl;”,先剥去传入类名首尾的”L”与”;”,以便将恶意数据暴露出来,再经过黑名单校验。
我们写个小测试,看看这俩字符是不是”L”与”;”
构造下图poc即可轻易绕过1.2.42版本
1.2.45版本漏洞
在Fastjson 1.2.45版本中,checkAutotype安全机制又被发现了一种绕过方式。
之前的几次绕过都是针对checkAutoType的绕过,而这次则是利用了一条黑名单中不包含的元素,从而绕过了黑名单限制。
本次绕过利用到的是mybatis库。如果想测试成功,需要额外安装mybatis库。下文测试用例中安装的版本是3.5.5。
首先简单介绍下mybatis,maven上的简介如下:“MyBatisSQL映射器框架使将关系数据库与面向对象的应用程序结合使用变得更加容易。MyBatis使用XML描述符或注释将对象与存储过程或SQL语句耦合。相对于对象关系映射工具,简单性是MyBatis数据映射器的最大优势。
@type指定了JndiDataSourceFactory类,而在properties属性中的data_source变量中指定恶意数据源。由于JndiDataSourceFactory并不在黑名单上,因此可以顺利通过黑名单校验,在接下来的反序列化过程中,在为Properties变量赋值时调用其setter方法,可见下图动态调试结果