前言
在fastjson1.2.25更新之后,新增了白名单校验,只有在白名单之中的类才能被反序列化,随后也出现了绕过,本文分析作者是如何进行修复的以及绕过方式。
代码分析
查看一下github的修改记录,发现把直接加载typeName删除了,新增了一个config.checkAutoType(typeName),多了一层校验。
我们将pom中的fastjson版本调整至1.2.25,然后将断点打在config.checkAutoType上。
测试代码的话我们就用下面这个
1 | String jsonStr="{\n" + |
先走到checkAutoType方法内的这一行,这里比较关键,这个方法会看我们给的类是否在其白名单内,若在class不为空,若不在class为空。我们的这个类显然不在白名单内。得到class=null。
最后走到了下面这一步,判断我们的类是否在黑名单中
而我们的className正好在这个拒绝列表里面,所以我们会得到autoType is not support的报错。
绕过的测试代码如下:
按照网上的说法是这个原理:
那么从代码层面看一下为什么?
从代码层面来看,fastjson解析器在遇到内部下面这样的构造时,会直接去反序列化生成@type的实例
1 | "a": { |
而此时的@type为java.lang.Class,在fastjson的白名单中,从下面代码可以看出这一点
之前我们的恶意类在this.deserializers.findClass(typeName)得到的返回值为null,因为其不在白名单中,而java.lang.Class得到的类还是java.lang.Class。最后直接返回。
随后得到了这个类的反序列化解析器MiscCodec,调用其deserialze进行解析。
parser.accept解析把”val”解析了出来,lexer.stringVal的值就是val,可以看到如果不是val,那么程序就会报错。
接下来又将val:后面对应的类也解析了出来,然后parser.parse()取出,赋值给了objVal。
最后把com.sun.rowset.JdbcRowSetImpl作为类进行了加载。跟入这个loadClass方法。
我们发现这个map中的内容好像似曾相识,这不就是checkAutoType方法中运用到的一个map吗,现在我们已经把我们的com.sun.rowset.JdbcRowSetImpl类加入到这个map中了。接下来的步骤就是将“b”:对应的类进行实例化了,主要还是看其checkAutoType那关能不能过,所以断点直接打在checkAutoType。
可以看到直接从map中读出了我们之前加入进入的JdbcRowSetImpl类
接下来就是一路绿灯,返回我们的恶意类,绕过了checkAutoType执行了恶意命令。