我曾经写过一篇文章
fastjson远程代码执行漏洞问题分析
文章曾经比较详细分析了 fastjson
在1.2.24以及之前版本存在远程代码执行高危安全漏洞的问题。
本文则是针对另一个漏洞的介绍和分析。
官方对这次漏洞的说明是这样的:
近日,阿里云应急响应中心监测到fastjson爆出远程拒绝服务漏洞,攻击者在请求中构造特定json字符串,可远程造成服务器内存和CPU等资源耗尽,最终拒绝服务。官方已发布公告说明,最新的1.2.60和带有sec06字符的版本不受影响,请使用到的用户尽快升级至安全版本。
fastjson处理x转义字符不当,攻击者在请求中构造特定json字符串可导致服务器内存和CPU等资源耗尽,最终拒绝服务。阿里云应急响应中心提醒fastjson用户尽快采取安全措施阻止漏洞攻击。
官方说的很明白了,我们根据这段说明来构造一个“死亡”字符串重现下。
public class App { static final String DEATH_STRING = "{/"a/":/"//x"; public static void main(String[] args) { try { JSON.parse(DEATH_STRING); } catch (Exception e) { e.printStackTrace(); } } }
运行这段代码,你会发现这段不足10行的代码居然可以导致OOM。
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.alibaba.fastjson.parser.JSONLexerBase.putChar(JSONLexerBase.java:5041) at com.alibaba.fastjson.parser.JSONLexerBase.scanString(JSONLexerBase.java:889) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:483) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1394) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1360) at com.alibaba.fastjson.JSON.parse(JSON.java:165) at com.alibaba.fastjson.JSON.parse(JSON.java:175) at com.alibaba.fastjson.JSON.parse(JSON.java:144) at com.app.App.main(App.java:21)
我们可以顺着这个异常信息,撸一撸源码,看看到底是哪里的BUG。
如上图所示,当解析到字符 x
时,因为是最后一个字符,所以x1和x2都是 /u001A
,也就是十进制的26。因为每次 char ch = this.next();
获取的都是26这个字符,然后就在第三张图的位置死循环了。
有人可能会问,
if (this.isEOF())
这个语句为啥没有生效,不是已经到了结尾了吗?那我们来看看 isEOF
的实现,
public boolean isEOF() { return this.bp == this.len || this.ch == 26 && this.bp + 1 == this.len; }
因为前面两次 next
操作,bp+1已经不等于len了,(你可以单步调试看看),所以 isEOF
方法永远返回 false
。
官方已经给出了解决方案,那就是升级 fastjson
到1.2.60或以上版本。
我觉得作为一个优秀的程序员,既然知其然,也要知其所以然。我们来看看阿里的优秀工程师是如何修复这个漏洞的。我们把fastjson升级到 1.2.60
,然后继续debug源码,发现代码变成了这样:
这种解决方案虽然简单粗暴,但是也是很有效的不是吗,哈哈!
com.alibaba.fastjson.JSONException: invalid escape character /x at com.alibaba.fastjson.parser.JSONLexerBase.scanString(JSONLexerBase.java:983) at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:483) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1397) at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1363) at com.alibaba.fastjson.JSON.parse(JSON.java:170) at com.alibaba.fastjson.JSON.parse(JSON.java:180) at com.alibaba.fastjson.JSON.parse(JSON.java:149) at com.app.App.main(App.java:25)