CVE-2024-32030 — Kafka UI 未授权 JRMP 反序列化远程代码执行¶
发布于 2026-06-06
未授权远程代码执行
未经身份验证的攻击者可通过注册一个将 JMX 指标端点指向攻击者控制的 JRMP 监听器的集群,在 Kafka UI JVM 内执行任意命令。无需凭据或任何预先访问权限。
| 属性 | 值 |
|---|---|
| Project | Kafka UI |
| 受影响组件 | kafka-ui-api — JMX 指标收集器(JmxMetricsRetriever) |
| Severity | HIGH |
| CVSS | 8.1 (AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| CWE | CWE-502, CWE-94 |
| 受影响版本 | 所有版本 ≤ v0.7.1 |
| 修复版本 | v0.7.2(补丁提交 83b5a60,PR #4427,发布于 2024-04-10) |
| 安全公告 | GHSL-2023-230 |
1. 漏洞概述¶
Kafka UI(provectus/kafka-ui)是一个用于管理 Apache Kafka 集群的开源 Spring Boot Web 应用程序。当动态配置功能开启时(官方 README 推荐添加 -Ddynamic.config.enabled=true 参数),HTTP API 接受未经身份验证的 PUT /api/config 请求来注册新的 Kafka 集群,请求中包含一个 metrics 字段,用于指定 JMX 主机和端口。Kafka UI 的 JMX 指标收集器随后通过 Java RMI 消息协议(JRMP)使用标准的 javax.management.remote.JMXConnector 连接到该主机和端口。
JRMP 在完成身份验证之前就对服务端响应进行了反序列化。由于 commons-collections:3.2.2 和 scala-library:2.13.9 作为传递性依赖同时存在于类路径中,攻击者可以构造一个两阶段反序列化攻击,在 Kafka UI JVM 内实现完整的远程代码执行。
根本原因¶
文件: kafka-ui-api/pom.xml
kafka-json-schema-serializer 依赖将 commons-collections:3.2.2 作为传递性依赖引入。Commons Collections 3.2.2 通过系统属性 org.apache.commons.collections.enableUnsafeSerialization 控制的开关来防御不安全的函数反序列化。然而,同样作为传递性依赖的 scala-library:2.13.9 提供了可以在反序列化过程中设置该系统属性的构建块。
第一阶段 — 通过 Scala 利用链绕过 CC 3.2.2 防护:
ConcurrentSkipListMap.readObject() 在反序列化键时会调用 comparator.compare()。通过将比较器设置为 scala.math.Ordering$IterableOrdering,将键设置为包含指向 scala.sys.SystemProperties.$anonfun$addOne$1 的 SerializedLambda 的 scala.collection.View$Fill 实例,反序列化链会调用 System.setProperty("org.apache.commons.collections.enableUnsafeSerialization", "true"),从而禁用防护。
第二阶段 — 通过 CommonsCollections7 执行任意命令:
防护被禁用后,第二个 JRMP 连接投递标准的 CommonsCollections7 利用链:Hashtable → LazyMap → ChainedTransformer → InvokerTransformer → Runtime.exec(...)。
修复方案(提交 83b5a60)从 kafka-json-schema-serializer 的依赖树中排除了 commons-collections:3.x,并用 commons-collections4:4.4 替换它。后者的函数类完全不实现 Serializable 接口,使整个 CC 利用链在结构上无法被组装:
--- a/kafka-ui-api/pom.xml
+++ b/kafka-ui-api/pom.xml
@@ -81,6 +81,12 @@
<groupId>io.confluent</groupId>
<artifactId>kafka-json-schema-serializer</artifactId>
<version>${confluent.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
...
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-collections4</artifactId>
+ <version>4.4</version>
+ </dependency>
2. 漏洞复现环境¶
复现环境是一个运行官方 provectuslabs/kafka-ui:v0.7.1 镜像的单个 Docker 容器。该镜像内置了完整的漏洞类路径,commons-collections-3.2.2.jar 和 scala-library-2.13.9.jar 均被打包在 JAR 的嵌套库目录中。动态配置 API 通过环境变量启用,未授权的集群注册入口随即可用。
启动时不需要真实的 Kafka 代理。漏洞利用脚本会单独启动一个攻击者控制的代理;在 JMX 收集运行之前,Kafka UI API 接受任意 bootstrapServers 值而不会主动连接验证。
下载环境文件: env.zip — 或直接访问单个文件:env/docker-compose.yml。
启动环境¶
服务暴露在 http://127.0.0.1:8080。容器名称为 cve-2024-32030-kafka-ui,JVM 以服务用户 kafkaui(pid 1)运行。
确认漏洞底座¶
启动后运行以下检查,验证当前运行的是未修补版本:
# 1. API 正常响应
curl -s http://127.0.0.1:8080/actuator/health
# 预期输出: {"status":"UP",...}
# 2. 认证已禁用,动态配置已激活
curl -s http://127.0.0.1:8080/api/config | grep -o '"type":"DISABLED"'
# 预期输出: "type":"DISABLED"
# 3. JVM 服务用户及漏洞类路径
docker exec cve-2024-32030-kafka-ui ps -o user,args -p 1
docker exec cve-2024-32030-kafka-ui sh -c 'unzip -l /kafka-ui-api.jar | grep -E "commons-collections-3.2.2|scala-library-2.13"'
# 预期输出: 用户为 kafkaui;列表中同时包含 commons-collections-3.2.2.jar 和 scala-library-2.13.x.jar
容器通过 extra_hosts: host.docker.internal:host-gateway 配置了 host.docker.internal 别名,使 Kafka UI JVM 可以向宿主机上运行的监听器发起出站 JRMP 连接。
3. 漏洞利用¶
下载利用文件: exploit.zip — 或浏览单个文件:exploit/run.sh、exploit/JrmpExploit.java。
攻击由编排脚本 exploit/run.sh 驱动,该脚本负责编译 JRMP 监听器、启动攻击者 Kafka 代理、注册恶意集群配置,并在退出时自动清理。
前置条件¶
- 漏洞 Kafka UI 容器正在运行(参见第 2 节)。
- 宿主机上 Docker 可用(脚本使用 Docker 编译并运行监听器)。
- 宿主机 TCP 端口
1718(JRMP 监听器)和9092(攻击者 Kafka 代理)空闲可用。
执行利用¶
生成一个本次运行专用的随机 nonce,再调用脚本:
| 参数 | 值 | 说明 |
|---|---|---|
target_url |
http://127.0.0.1:8080 |
Kafka UI HTTP API(未授权) |
jrmp_port |
1718 |
JRMP 监听器的宿主机端口 |
marker_path |
/tmp/pwned |
标记文件路径前缀;利用程序写入 /tmp/pwned.<nonce> |
nonce |
poc<epoch><rand> |
本次运行专用 nonce——防止预先植入或过期文件通过检查 |
脚本执行步骤¶
- 在
eclipse-temurin:17-jdk容器中,使用目标程序自带的scala-library-2.13.9.jar和commons-collections-3.2.2.jar(存放在exploit/libs/)编译exploit/JrmpExploit.java。 - 启动攻击者 Kafka 代理(
apache/kafka:3.7.0,容器名cve-2024-32030-poc-broker),广播地址为host.docker.internal:9092,使 Kafka UI 的管理客户端能够发现代理节点,以便在触发 JMX 收集之前建立连接。 - 在宿主机端口
1718上启动 JRMP 监听器。 - 发送
PUT /api/config——一个未授权请求,注册一个集群,其metrics字段(type: JMX,host: host.docker.internal,port: 1718)将 JVM 的 JMX 客户端定向至攻击者监听器。 - 等待 Kafka UI 30 秒一次的定时指标收集周期。第一个 JMX 连接接收第一阶段载荷(Scala 利用链设置绕过属性);第二个连接接收第二阶段载荷(CC7 利用链调用
Runtime.exec("touch /tmp/pwned.<NONCE>"))。 - 退出时通过 shell trap 清理监听器和代理容器。
两个调度周期完成大约需要两分钟,请耐心等待。
确认执行成功——独立验证¶
验证通过完全独立于利用脚本输出的渠道进行:直接对 Kafka UI 容器执行 shell 命令,检查标记文件是否由 Kafka UI JVM 创建:
docker exec cve-2024-32030-kafka-ui stat -c '%U %n' /tmp/pwned.<NONCE>
# 预期输出: kafkaui /tmp/pwned.<NONCE>
在已验证的复现运行中:
docker exec cve-2024-32030-kafka-ui ls -l /tmp/pwned.poc178039439726378
-> -rw-r--r-- 1 kafkaui kafkaui 0 Jun 2 10:01 /tmp/pwned.poc178039439726378
docker exec cve-2024-32030-kafka-ui stat -c '%U %n' /tmp/pwned.poc178039439726378
-> kafkaui /tmp/pwned.poc178039439726378
这两个属性共同确认任意代码在漏洞进程内部得到执行:
- nonce 是新鲜的 — 标记文件携带仅为本次运行生成的 nonce;任何预先植入或过期的文件都无法匹配它。
- 所有者是
kafkaui— 文件由 JVM 的Runtime.exec创建,而Runtime.exec在 JVM 自身身份(pid 1,用户kafkaui)下运行。利用脚本从未直接写入容器文件系统,也没有与容器共享任何卷。
辅助证据(非必要):监听器日志记录了 call #1 -> delivering STAGE 1 (set property),随后是 call #2 -> delivering STAGE 2 (CC7 exec)。Kafka UI 自身日志显示,在利用链触发的瞬间,JmxMetricsRetriever.withJmxConnector 处抛出了 java.io.StreamCorruptedException——利用链在漏洞 JMX 帧的反序列化过程中执行后,该异常随即抛出。修复补丁移除的正是这条异常路径。
环境清理¶
该命令停止并移除 Kafka UI 容器,同时清理所有卷(包括标记文件)。
4. 安全建议¶
修复方案¶
升级至 Kafka UI v0.7.2 或更高版本。修复补丁于 2024-04-10 发布(提交 83b5a60,PR #4427)。该修复从传递性依赖树中移除了 commons-collections:3.2.2,并用 commons-collections4:4.4 替换。后者的函数类完全不实现 Serializable 接口,无论系统属性状态如何,Scala 绕过利用链和 CC7 远程代码执行利用链均无法从类路径中组装。
如果无法立即升级,可采取以下缓解措施:
- 禁用动态集群配置。 移除或省略
-Ddynamic.config.enabled=true参数。没有该参数,新集群无法通过未授权的 HTTP API 注册,从而阻断主要攻击入口。集群只能通过应用程序配置文件静态定义,而只有具有文件系统访问权限的管理员才能添加。 - 启用身份验证。 Kafka UI 支持多种身份验证机制(OAuth2、LDAP、基本认证)。启用其中任何一种均可阻止未授权的
PUT /api/config请求,将攻击门槛从仅需网络访问提升至需要有效凭据。 - 限制网络访问。 如果 Kafka UI 实例仅供内部使用,可通过防火墙或服务网格规则限制能够访问 8080 端口的主机,即使没有应用层控制也能降低攻击面。
禁用动态配置或添加身份验证只能移除未授权注册入口,并不会从类路径中删除利用链类。若攻击者对目标 Kafka UI 已配置监控的 Kafka 代理拥有网络层访问权限,仍可直接操控 JMX 响应触发漏洞。升级至 v0.7.2 是唯一完整的修复方案。
参考资料¶
- NVD CVE-2024-32030 — CWE、CVSS、受影响版本
- GitHub Security Lab 安全公告 GHSL-2023-230 — 原始报告者;JMX 反序列化描述
- 修复提交 83b5a60 — 精确差异:排除
commons-collections:3.x,添加commons-collections4:4.4 - 修复 PR #4427 — "added commons-collections4 library instead of commons-collections",合并于 2024-04-08
- v0.7.2 发版说明 — "CVEs fixes Apr 2024"
- 华为 PSIRT 技术分析(镜像) — 完整的 Scala1 利用链载荷代码,以及逐步复现 CC7 RCE 的截图
- Nuclei 模板 PoC — 仅用于检测的模板
- Apache Commons Collections 安全报告 — 解释了 CC 3.2.2 中被 Scala 利用链绕过的
enableUnsafeSerialization防护机制