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