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