高唐企业建网站服务商,做鞋原料网站,四川住房城乡建设网站,新开传奇网站服参考#xff1a;fastjson到底做错了什么#xff1f;为什么会被频繁爆出漏洞#xff1f;_Sitin涛哥的博客-CSDN博客 fastjson结合JdbcRowSetImpl反序列化利用理解 - 知乎
工具猴-免费在线工具-在线工具箱-
fastjson大家一定都不陌生#xff0c;这是阿里巴巴的开源一个JSON…参考fastjson到底做错了什么为什么会被频繁爆出漏洞_Sitin涛哥的博客-CSDN博客 fastjson结合JdbcRowSetImpl反序列化利用理解 - 知乎
工具猴-免费在线工具-在线工具箱-
fastjson大家一定都不陌生这是阿里巴巴的开源一个JSON解析库通常被用于将Java Bean和JSON 字符串之间进行转换。
前段时间fastjson被爆出过多次存在漏洞很多文章报道了这件事儿并且给出了升级建议。
但是作为一个开发者我更关注的是他为什么会频繁被爆漏洞于是我带着疑惑去看了下fastjson的releaseNote以及部分源代码。
最终发现这其实和fastjson中的一个AutoType特性有关。
从2019年7月份发布的v1.2.59一直到2020年6月份发布的 v1.2.71 每个版本的升级中都有关于AutoType的升级。
下面是fastjson的官方releaseNotes 中几次关于AutoType的重要升级 1.2.59发布增强AutoType打开时的安全性 fastjson 1.2.60发布增加了AutoType黑名单修复拒绝服务安全问题 fastjson 1.2.61发布增加AutoType安全黑名单 fastjson 1.2.62发布增加AutoType黑名单、增强日期反序列化和JSONPath fastjson 1.2.66发布Bug修复安全加固并且做安全加固补充了AutoType黑名单 fastjson 1.2.67发布Bug修复安全加固补充了AutoType黑名单 fastjson 1.2.68发布支持GEOJSON补充了AutoType黑名单。引入一个safeMode的配置配置safeMode后无论白名单和黑名单都不支持autoType。 fastjson 1.2.69发布修复新发现高危AutoType开关绕过安全漏洞补充了AutoType黑名单 fastjson 1.2.70发布提升兼容性补充了AutoType黑名单 甚至在fastjson的开源库中有一个Isuue是建议作者提供不带autoType的版本 那么什么是AutoType为什么fastjson要引入AutoType为什么AutoType会导致安全漏洞呢本文就来深入分析一下。
AutoType 何方神圣
fastjson的主要功能就是将Java Bean序列化成JSON字符串这样得到字符串之后就可以通过数据库等方式进行持久化了。
但是fastjson在序列化以及反序列化的过程中并没有使用Java自带的序列化机制而是自定义了一套机制。
其实对于JSON框架来说想要把一个Java对象转换成字符串可以有两种选择 1、基于属性 2、基于setter/getter
而我们所常用的JSON序列化框架中FastJson和jackson在把对象序列化成json字符串的时候是通过遍历出该类中的所有getter方法进行的。Gson并不是这么做的他是通过反射遍历该类中的所有属性并把其值序列化成json。
假设我们有以下一个Java类
class Store {private String name;private Fruit fruit;public String getName() {return name;}public void setName(String name) {this.name name;}public Fruit getFruit() {return fruit;}public void setFruit(Fruit fruit) {this.fruit fruit;}}interface Fruit {}class Apple implements Fruit {private BigDecimal price;//省略 setter/getter、toString等}
当我们要对他进行序列化的时候fastjson会扫描其中的getter方法即找到getName和getFruit这时候就会将name和fruit两个字段的值序列化到JSON字符串中。
那么问题来了我们上面的定义的Fruit只是一个接口序列化的时候fastjson能够把属性值正确序列化出来吗如果可以的话那么反序列化的时候fastjson会把这个fruit反序列化成什么类型呢
我们尝试着验证一下基于(fastjson v 1.2.68)
Store store new Store();store.setName(Hollis);Apple apple new Apple();apple.setPrice(new BigDecimal(0.5));store.setFruit(apple);String jsonString JSON.toJSONString(store);System.out.println(toJSONString : jsonString);
以上代码比较简单我们创建了一个store为他指定了名称并且创建了一个Fruit的子类型Apple然后将这个store使用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{nameHollis, 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}
可以看到使用SerializerFeature.WriteClassName进行标记后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{nameHollis, fruitApple{price0.5}}getFruit : Apple{price0.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前后的L和;形如Lcom.lang.Thread;。 而黑白名单又是通过startWith检测的那么黑客只要在自己想要使用的攻击类库前后加上L和;就可以绕过黑白名单的检查了也不耽误被fastjson正常加载。
如Lcom.sun.rowset.JdbcRowSetImpl;会先通过白名单校验然后fastjson在加载类的时候会去掉前后的L和变成了com.sun.rowset.JdbcRowSetImpl。
为了避免被攻击在之后的 v1.2.42版本中在进行黑白名单检测的时候fastjson先判断目标类的类名的前后是不是L和;如果是的话就截取掉前后的L和;再进行黑白名单的校验。
看似解决了问题但是黑客发现了这个规则之后就在攻击时在目标类前后双写LL和;;这样再被截取之后还是可以绕过检测。如LLcom.sun.rowset.JdbcRowSetImpl;;
魔高一尺道高一丈。在 v1.2.43中fastjson这次在黑白名单判断之前增加了一个是否以LL未开头的判断如果目标类以LL开头那么就直接抛异常于是就又短暂的修复了这个漏洞。
黑客在L和;这里走不通了于是想办法从其他地方下手因为fastjson在加载类的时候不只对L和;这样的类进行特殊处理还对[也被特殊处理了。
同样的攻击手段在目标类前面添加[v1.2.43以前的所有版本又沦陷了。
于是在 v1.2.44版本中fastjson的作者做了更加严格的要求只要目标类以[开头或者以;结尾都直接抛异常。也就解决了 v1.2.43及历史版本中发现的bug。
在之后的几个版本中黑客的主要的攻击方式就是绕过黑名单了而fastjson也在不断的完善自己的黑名单。
autoType不开启也能被攻击
但是好景不长在升级到 v1.2.47 版本时黑客再次找到了办法来攻击。而且这个攻击只有在autoType关闭的时候才生效。
是不是很奇怪autoType不开启反而会被攻击。
因为在fastjson中有一个全局缓存在类加载的时候如果autotype没开启会先尝试从缓存中获取类如果缓存中有则直接返回。黑客正是利用这里机制进行了攻击。
黑客先想办法把一个类加到缓存中然后再次执行的时候就可以绕过黑白名单检测了多么聪明的手段。
首先想要把一个黑名单中的类加到缓存中需要使用一个不在黑名单中的类这个类就是java.lang.Class
java.lang.Class类对应的deserializer为MiscCodec反序列化时会取json串中的val值并加载这个val对应的类。 如果fastjson cache为true就会缓存这个val对应的class到全局缓存中 如果再次加载val名称的类并且autotype没开启下一步就是会尝试从全局缓存中获取这个class进而进行攻击。
所以黑客只需要把攻击类伪装一下就行了如下格式
{type: java.lang.Class,val: com.sun.rowset.JdbcRowSetImpl}于是在 v1.2.48中fastjson修复了这个bug在MiscCodec中处理Class类的地方设置了fastjson cache为false这样攻击类就不会被缓存了也就不会被获取到了。
在之后的多个版本中黑客与fastjson又继续一直都在绕过黑名单、添加黑名单中进行周旋。
直到后来黑客在 v1.2.68之前的版本中又发现了一个新的漏洞利用方式。
利用异常进行攻击
在fastjson中 如果type 指定的类为 Throwable 的子类那对应的反序列化处理类就会使用到 ThrowableDeserializer
而在ThrowableDeserializer#deserialze的方法中当有一个字段的key也是 type时就会把这个 value 当做类名然后进行一次 checkAutoType 检测。
并且指定了expectClass为Throwable.class但是在checkAutoType中有这样一约定那就是如果指定了expectClass 那么也会通过校验。 因为fastjson在反序列化的时候会尝试执行里面的getter方法而Exception类中都有一个getMessage方法。
黑客只需要自定义一个异常并且重写其getMessage就达到了攻击的目的。
这个漏洞就是6月份全网疯传的那个严重漏洞使得很多开发者不得不升级到新版本。
这个漏洞在 v1.2.69中被修复主要修复方式是对于需要过滤掉的expectClass进行了修改新增了4个新的类并且将原来的Class类型的判断修改为hash的判断。
其实根据fastjson的官方文档介绍即使不升级到新版在v1.2.68中也可以规避掉这个问题那就是使用safeMode
AutoType 安全模式
可以看到这些漏洞的利用几乎都是围绕AutoType来的于是在 v1.2.68版本中引入了safeMode配置safeMode后无论白名单和黑名单都不支持autoType可一定程度上缓解反序列化Gadgets类变种攻击。
设置了safeMode后type 字段不再生效即当解析形如{type: com.java.class}的JSON串时将不再反序列化出对应的类。
开启safeMode方式如下
ParserConfig.getGlobalInstance().setSafeMode(true);如在本文的最开始的代码示例中使用以上代码开启safeMode模式执行代码会得到以下异常
Exception in thread main com.alibaba.fastjson.JSONException: safeMode not support autoType : com.hollis.lab.fastjson.test.Appleat com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1244)
但是值得注意的是使用这个功能fastjson会直接禁用autoType功能即在checkAutoType方法中直接抛出一个异常。 后话
目前fastjson已经发布到了 v1.2.72版本历史版本中存在的已知问题在新版本中均已修复。
开发者可以将自己项目中使用的fastjson升级到最新版并且如果代码中不需要用到AutoType的话可以考虑使用safeMode但是要评估下对历史代码的影响。
因为fastjson自己定义了序列化工具类并且使用asm技术避免反射、使用缓存、并且做了很多算法优化等方式大大提升了序列化及反序列化的效率。
之前有网友对比过 当然快的同时也带来了一些安全性问题这是不可否认的。
最后其实我还想说几句虽然fastjson是阿里巴巴开源出来的但是据我所知这个项目大部分时间都是其作者温少一个人在靠业余时间维护的。
知乎上有网友说温少几乎凭一己之力撑起了一个被广泛使用JSON库而其他库几乎都是靠一整个团队就凭这一点温少作为“初心不改的阿里初代开源人”当之无愧。
其实关于fastjson漏洞的问题阿里内部也有很多人诟病过但是诟病之后大家更多的是给予理解和包容。
fastjson目前是国产类库中比较出名的一个可以说是倍受关注所以渐渐成了安全研究的重点所以会有一些深度的漏洞被发现。就像温少自己说的那样
和发现漏洞相比更糟糕的是有漏洞不知道被人利用。及时发现漏洞并升级版本修复是安全能力的一个体现。
就在我写这篇文章的时候在钉钉上问了温少一个问题他竟然秒回这令我很惊讶。因为那天是周末周末钉钉可以做到秒回这说明了什么
他大概率是在利用自己的业余维护fastjson吧…
最后知道了fastjson历史上很多漏洞产生的原因之后其实对我自己来说我是更加敢用fastjson了…
致敬fastjson致敬安全研究者致敬温少
参考资料
https://github.com/alibaba/fastjson/releases
https://github.com/alibaba/fastjson/wiki/security_update_20200601
https://paper.seebug.org/1192/
https://mp.weixin.qq.com/s/EXnXCy5NoGIgpFjRGfL3wQ
http://www.lmxspace.com/2019/06/29/FastJson-反序列化学习