Log4j漏洞原始碼分析
-
這幾天Log4j的問題訊息滿天飛,今天我們就一起來看看從原始碼角度看看這個漏洞是如何產生的。
-
大家都知道這次問題主要是由於Log4j中提供的jndi的功能。
具體涉及到的入口類是log4j-core-xxx.jar中的org.apache.logging.log4j.core.lookup.StrSubstitutor
這個類。
原因是Log4j提供了Lookups的能力(關於Lookups可以點這裡去看官方文件的介紹),簡單來說就是變數替換的能力。
在Log4j將要輸出的日誌拼接成字串之後,它會去判斷字串中是否包含${
和}
,如果包含了,就會當作變數交給org.apache.logging.log4j.core.lookup.StrSubstitutor
這個類去處理。
相關的程式碼下面這個
首先是org.apache.logging.log4j.core.pattern.MessagePatternConverter
這個類的format
方法
圖中標註1的地方就是現在漏洞修復的地方,讓noLookups這個變數為true,就不會進去裡面的邏輯,也就沒有這個問題了。
圖中標註2的地方就是判斷字串中是否包含
${
,如果包含,就將從這個字元開始一直到字串結束,交給圖中標註3的地方去進行替換。圖中標註3的地方就是具體執行替換的地方,其中
config.getStrSubstitutor()
就是我們上面提到的org.apache.logging.log4j.core.lookup.StrSubstitutor
。
在StrSubstitutor
中,首先將${
和}
之間的內容提取出來,交給resolveVariable
這個方法來處理。
我們看下resolver
的內容,它是org.apache.logging.log4j.core.lookup.Interpolator
類的物件。
它的lookups定義了10中處理型別,還有一個預設的defaultLoopup,一種11中。如果能匹配到10中處理型別,就交給它們去處理,其他的都會交給defaultLookup去處理。
匹配規則也很簡單,下面簡單舉個例子。
1.如果我們的日誌內容中有${jndi:rmi://127.0.0.1:1099/hello}這些內容,去掉
${
和}
,傳遞給resolver
的就是jndi:rmi://127.0.0.1:1099/hello。2.
resolver
會將第一個:之前的內容和lookups做匹配,我們這裡獲取到的是jndi
,就會將剩餘部分jndi:rmi://127.0.0.1:1099/hello交給jdni
的處理器JndiLookup
去處理。
圖中標註1的地方入參就是jndi:rmi://127.0.0.1:1099/hello
圖中標註2的地方就是jndi
圖中標註3的地方就是rmi://127.0.0.1:1099/hello
圖中標註4的地方就是處理器
JndiLookup
類的物件圖中標註5的地方就是jndi來處理的入口
關於jndi相關的,以及漏洞如何復現網上有一大把的教程,這裡就不展開了。
- 關於漏洞的修復。
主要是通過設定noLookups變數的值,不讓它進去這個if
裡面的邏輯。
這個變數的值是來自下面這個屬性
所以在在程式碼中加入System.setProperty("log4j2.formatMsgNoLookups","true");
這句也就可以了。當然網上有更多其他的修復方法,這裡就不討論了。