Apache Kafka 远程代码执行漏洞复现及攻击拦截 (CVE-2023-25194)
本文转自云鲨RASP 并作补充
漏洞简介
Apache Kafka是一个分布式数据流处理平台,可以实时发布、订阅、存储和处理数据流。Kafka Connect是一种用于在kafka和其他系统之间可扩展、可靠的流式传输数据的工具。攻击者可以利用基于SASLJAAS 配置和SASL 协议的任意Kafka客户端,对Kafka Connect worker 创建或修改连接器时,通过构造特殊的配置,进行JNDI 注入来实现远程代码执行。
影响版本
2.4.0<=Apache kafka<=3.3.2
修复方案
更新Apache Kafka至官方最新版本
环境搭建
漏洞复现
exp可参考:
发起攻击请求:
构造payload ,执行新建/tmp/test.txt文件
验证漏洞存在,文件新建成功
开启RASP后发起攻击:
在业务优先模式下,RASP会出现JNDI注入的告警,拦截最终的命令执行
堆栈信息为
在防护模式下将直接在JNDI注入处被拦截
堆栈信息为
漏洞分析
开始
1 org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Properties)跟进到
1 org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Propertiesorg.apache.kafka.common.serialization.Serializer<K>,org.apache.kafka.common.serialization.Serializer<V>)调用
1 org.apache.kafka.common.utils.Utils#propsToMap对传入对象进行处理
将map型的对象传入
1 org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(java.util.Map<java.lang.String,java.lang.Object>org.apache.kafka.common.serialization.Serializer<K>org.apache.kafka.common.serialization.Serializer<V>)之后调用
1 org.apache.kafka.clients.producer.ProducerConfig#appendSerializerToConfig将返回的newConfigs传入
1 org.apache.kafka.clients.producer.ProducerConfig#ProducerConfig(java.util.Map<java.lang.String,java.lang.Object>)将配置参数传入
1 org.apache.kafka.clients.producer.KafkaProducer#KafkaProducer(org.apache.kafka.clients.producer.ProducerConfig,org.apache.kafka.common.serialization.Serializer<K>org.apache.kafka.common.serialization.Serializer<V>org.apache.kafka.clients.producer.internals.ProducerMetadata,org.apache.kafka.clients.KafkaClientorg.apache.kafka.clients.producer.internals.ProducerInterceptors<K,V>org.apache.kafka.common.utils.Time)赋值后调用
1 org.apache.kafka.clients.producer.KafkaProducer#newSender调用到
1 org.apache.kafka.clients.ClientUtils#createChannelBuilder赋值后调用
1 org.apache.kafka.common.network.ChannelBuilders#clientChannelBuilder这里对值做了一个判断后调用
1 org.apache.kafka.common.network.ChannelBuilders#createCreate方法中得到map型的configs后进行switch,得到SaslChannelBuilder类型channelBuilder的对象,switch结束后调用了
1 org.apache.kafka.common.network.SaslChannelBuilder#configure
1 org.apache.kafka.common.network.SaslChannelBuilder#configure进入循环后到
1 org.apache.kafka.common.security.authenticator.LoginManager#acquireLoginManager判断值后到
1 org.apache.kafka.common.security.authenticator.LoginManager#LoginManager跟进到
1 org.apache.kafka.common.security.authenticator.AbstractLogin#login调用
1 javax.security.auth.login.LoginContext#login调用
1 javax.security.auth.login.LoginContext#invokePriv调用
1 javax.security.auth.login.LoginContext#invoke进行逻辑判断后调用initialize方法
Initialize中得到userProvider
user.provider.url通过jndi提供
调用
1 com.sun.security.auth.module.JndiLoginModule#login调用
1 com.sun.security.auth.module.JndiLoginModule#attemptAuthentication通过
1 javax.naming.InitialContext#lookup(java.lang.String)执行userProvider的值
由于RASP对javax.naming.InitialContext.lookup调用做了防护策略检测,所以会在此处拦截。