每次規模比較大的漏洞,JNDI好像都不會缺席。最近人盡皆知的Log4j2漏洞也和它有關,讓人 不由得懷疑,是不是作者開的后門。
因為JNDI這個玩意,別說用過,很多人連聽都沒聽說過。這么冷門酸爽的東西,有什么理由把它放在一個日志框架里呢?恐怕只有作者想得通。
數據庫驅動
很多人接觸JNDI,是從數據庫的驅動開始的。當然,隨著SpringBoot單體發布模式的流行,現在用這種方式來獲取數據庫配置的古董公司,是越來越少了。
比如,我們可以在tomcat得server.xml里,配置一個叫做xjjdogDB的資源。
- <Resource name="jdbc/xjjdogDB" auth="Container" type="javax.sql.DataSource"
- maxTotal="100" maxIdle="30" maxWaitMillis="10000"
- username="xjjdog" password="123456" driverClassName="com.mysql.jdbc.Driver"
- url="jdbc:mysql://localhost:3306/xjjdog_db"/>
那么,我們只需要在SpringBoot中配置上JNDI這個名字,它就能加載正確的配置。前提是我們需要把SpringBoot服務打包成WAR包發布。像JBoss這樣的宣稱企業級服務器的軟件,就喜歡這么干。
- spring:
- datasource:
- jndi-name: jdbc/xjjdogDB
從這里,我們可以看出。JNDI到底是個神馬玩意呢?你可以認為它是一個配置中心,也可以認為它是一個命名服務。其根本功能,就是讓你可以通過一個簡短的字符串,就能獲取一系列的復雜配置和初始化后的功能。
這樣,我們就可以避免將這些配置直接寫在項目里。程序啟動起來,到底加載的什么東西,要看運行的環境配置的什么東西。
具體實現的話,不就是一個可以根據key獲取value的HashMap嘛。
危險由此而來
關鍵是這個value,它不是String,它是一個Object。要從字符串變身為一個正常的類,還要做到通用,那就不得不依靠反射。
這張圖是Oracle官方的一張關于JNDI的介紹。上面的都不是關鍵,關鍵的地方在于,JNDI通過SPI機制,可以和LDAP、RMI等各種技術,產生聯動。
任何為了追求方便而不遵守常規的便捷通道,都會產生問題。SPI是為數不多的打破Java類加載機制的技術,和Unsafe類一樣,強大但并不那么推薦使用。
如上圖,就是NamingManager類里面的方法getObjectFactoryFromReference。當它從本地加載相應類的時候,如果加載不到,它干了一件不該干但又不得不干的事情,那就是從網絡上的代碼里面構造相應的對象!
這下,在炫肌肉的同時,可完犢子了。如上圖,我們只需要在8000端口啟動一個nginx。或者直接使用python啟動一個小小的web服務器。
- python -m http.server 8000
能夠發起外網請求的服務器,將會乖乖的自動從我們指定的服務器上撈下非法的class文件進行加載。
制作這些攻擊性的playload也非常容易,有marshalsec這樣的工具,可以很方便的生成。
根據java的類加載機制,在static代碼塊里面,就可以干一些實際的代碼執行邏輯。我們只需要把編譯后的a.class放在該放的地方,JNDI這玩意就能夠加載它。
- public class a {
- static {
- try {
- String[] cmd = {"calc.exe"};
- java.lang.Runtime.getRuntime().exec(cmd).waitFor();
- } catch ( Exception e ) {
- e.printStackTrace();
- }
- }
- }
上面的代碼將會在你部署的服務器上啟動一個calc計算器。當然,它還可以干更多事情。
END
從上面可以看出,Java的反射很強大,但是也很危險。SPI功能繼承了這個特性,勇猛的暴露了它的弱點。我覺得功能很好啊,但它為什么要存在于日志框架呢?
這可能是因為卷吧。畢竟一個日志框架,也是要有元宇宙的夢想的!
原文地址:https://mp.weixin.qq.com/s?__biz=MzA4MTc4NTUxNQ==&mid=2650525085&idx=1&sn=38cdf800ec574d1f7990ec019b49f0a2&chksm=8780ca59b0f7434fd22831ef9be29e37976c5ba8a4b95bc3fafa3d75654dbb4596237dfeb6ce&mpshare=1&s