引言
我曾經寫過一篇文章
文章曾經比較詳細分析了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)