作者:icematcha@云影实验室
近日,Fastjson被传爆出新的“0day”,也引起了大家的高度关注。云影实验室第一时间进行了跟进和分析,通过分析我们发现,此次的漏洞并不算新的漏洞,其实官方早在去年五月就已经推出了相关补丁修复,但可能没有发布相关的安全通告,导致许多用户没有及时升级处理,而在此次护网中就谣传变成了现在的“0day”。
此次漏洞利用的核心点是java.lang.class这个java的基础类,在fastjson 1.2.48以前的版本没有做该类做任何限制,加上代码的一些逻辑缺陷,造成黑名单以及autotype的绕过。下面跟着代码看看这个类的具体应用。
Fastjson在开始解析json前会优先加载配置,在加载配置时会调用TypeUtils的addBaseClassMappings和loadClass方法将一些经常会用到的基础类和三方库存放到一个ConcurrentMap对象mappings中,类似于缓存机制。
配置加载完成后正式开始解析json数据,在ParserConfig.checkAutoType方法中对’@type’对应的value进行处理。fastjson在1.2.2+版本默认便不再开启autoType选项,且在调用解析函数时我们没有传入预期的反序列化对象的对应类名时,fastjson则通过从mappings中或者deserializers.findClass()方法来获取反序列化对象的对应类。
找到对应类后便返回,不再经过黑名单和autotype的检查流程。而java.lang.class该类恰好存在于deserializers对象的buckets属性中:
接着获取到java.lang.class对应的反序列化处理类MiscCodec:
在MiscCodec类中,java.lang.class拥有加载任意类到mappings中的功能。首先从输入的json串中解析获取val对应的键值:
获取后调用前面提到的TypeUtils. loadClass()方法对该键值进行类加载操作:
在TypeUtils. loadClass()中,我们就可以看到当cache参数为true时,将键值对应的类名放到了mappings中后返回:
Mappings是ConcurrentMap类的,顾名思义就是在当前连接会话生效。所以我们需要在一次连接会话同时传入两个json键值对时,此次连接未断开时,继续解析第二个json键值对。此时我们利用’@type’传入早已在黑名单中的com.sun.rowset.JdbcRowSetImpl类尝试jdni注入利用:
可以看到fastjson直接从mappings中获取到了该类,并在做有效判断后,直接返回了:
此时的mappings中确实存在com.sun.rowset.JdbcRowSetImpl类:
而就像前面已经提到的,黑名单以及autotype开关的检查是在上面那处return之后的,所以也就变相的绕过了黑名单以及autotype开关的检查。
绕过了黑名单和autotype,剩下的利用过程就跟以前的利用完全一致了,利用jdni注入来RCE:
所以最终的漏洞利用其实是分为两个步骤,第一步利用java.lang.class加载黑名单类到mappings中,第二步直接从mappings中取出黑名单类完成漏洞利用。
回头来看看,我觉得该漏洞更像是一个逻辑漏洞,而不是依赖一些硬性的漏洞利用点,是代码的运行逻辑缺陷造成的漏洞利用。
在1.2.48版本的补丁中,首先黑名单做了更新,加了两条,其中应该包含了java.lang.class这个类:
MiscCodec中也将传入的cache参数置为了false:
ParserConfig.checkAutoType()也调整了检查策略和逻辑。
Fastjson在1.2.47及以下的版本受影响,请及时升级到1.2.48及以上版本。
https://github.com/alibaba/fastjson