问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
全网首发!CVE-2025-27817 Apache Kafka Client 任意文件读取与SSRF 漏洞分析复现
漏洞分析
CVE-2025-27817
CVE-2025-27817 ============== 一、漏洞成因 ------ 该漏洞源于Apache Kafka Client在配置SASL/OAUTHBEARER连接时,对sasl.oauthbearer.token.endpoint.url和sasl.oauthbearer.jwks.endpoint.url参数的安全控制存在缺陷。攻击者可通过构造恶意URL参数,利用该缺陷实现任意文件读取或发起SSRF请求(访问非预期目标地址)。 二、影响版本 ------ 3.1.0 <= Apache Kafka <= 3.9.0 三、漏洞复现 ------   四、漏洞分析 ------ 根据漏洞触发点所对应的路由 connectors,可定位至以下相关代码:  147-152行代码对传入的信息处理,以及创建示例等,漏洞入口是: ```java herder.putConnectorConfig(name, configs, createRequest.initialTargetState(), false, cb); ``` 继续跟进putConnectorConfig方法, 这里会跟到两个类:*StandaloneHerder* 和 *DistributedHerder*,之前说的修复点也确实是这里,基本可以确定这是入口点,我们看*DistributedHerder*吧 ```java @Override public void putConnectorConfig(final String connName, final Map config, final TargetState targetState, final boolean allowReplace, final Callback> callback) { log.trace("Submitting connector config write request {}", connName); addRequest( () -> { doPutConnectorConfig(connName, config, targetState, allowReplace, callback); return null; }, forwardErrorAndTickThreadStages(callback) ); } ``` 继续跟进*doPutConnectorConfig*方法,这里的config,就是先前connector路由传的json数据  然后跟进validateConnectorConfig方法  进入validateConnectorConfig方法,到现在,逻辑检验都是普通的,所以就不多赘述了,继续跟进  调用了: ```java protected Connector getConnector(String connType) { return tempConnectors.computeIfAbsent(connType, k -> plugins().newConnector(k)); } ``` 利用 plugins().newConnector() 动态加载类,例如:MirrorSourceConnector  这里可以看到根据json数据的config\['\***'\]=\*\***.MirrorSourceConnector调用MirrorSourceConnector这个Connector用于后续的执行 后续跟进的话可以看到通过反射调用Connector: ```java Class<?> klass = loader.loadClass(classOrAlias, false); ``` 回到AbstractHerder类,继续分析接下来的逻辑  接下来就是确定connectorType为sink or source,然后对数据进行处理等,可以自己看  在`java config = connector.validate(connectorProps);` 跟进: ```java @Override public org.apache.kafka.common.config.Config validate(Map props) { List configValues = super.validate(props).configValues(); validateExactlyOnceConfigs(props, configValues); validateEmitOffsetSyncConfigs(props, configValues); return new org.apache.kafka.common.config.Config(configValues); } ``` 接下来进入到validate验证阶段,这里就很绕了,我们知道是MirrorSourceConnector这个Connector创建了Tasks,所以他肯定要进到这个方法  可以看到过了validate初始验证,进入到start方法,继续跟进,由于漏洞触发点是认证相关,我们断点在认证的各个方法,一个一个看  这里进入到认证阶段forwardingAdmin  实例化ForwardingAdmin实现类  通过json数据中的值: ```json "****": "SASL_PLAINTEXT", "****": "OAUTHBEARER", "****": "****.OAuthBearerLoginCallbackHandler", ``` 来调用相关认证方法,然后调用create方法(漏洞触发点)  通过 ```java URL tokenEndpointUrl = cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL); ``` 获取json中的*sasl.oauthbearer.token.endpoint.url*,继续跟进  这里可以看到`java accessTokenRetriever.retrieve();`返回了文件信息,我们向上追踪  发现需要传参,全局搜new FileTokenRetriever(  发现就在Create方法内,尴尬...... 好,利用链如下: ```markdown [1] 用户提交 Connector 配置请求(HTTP API) | |--> REST API: Connect REST `/connectors` 接口处理 connector 配置 | [2] WorkerConfig / ConnectorConfig 解析配置(Map) | [3] validateConnectorConfig(...) 进行 connector 配置验证 | [4] connector.config() -> 返回 ConfigDef | |--> connector.validate(...)(触发 MirrorSourceConnector.validate()) | |--> validateExactlyOnceConfigs(...) |--> validateEmitOffsetSyncConfigs(...) | [5] connector.start(props) | |--> new MirrorSourceConfig(props) | |--> super(props) --> AbstractConfig 初始化 | |--> createAdmin(...)(构造 ForwardingAdmin) | |--> forwardingAdmin(config) | |--> get(FORWARDING_ADMIN_CLASS) |--> Utils.newParameterizedInstance(...) | |--> KafkaMirrorMakerClientBasedAdmin.create(...) | |--> OAuthBearerLoginModule / SaslClientAuthenticator 初始化 | |--> AccessTokenRetriever.create(...) | |--> cu.validateUrl(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL) | |--> protocol == "file" ? --> new FileTokenRetriever(Path) | |--> init() | |--> Utils.readFileAsString(path) | |--> Files.readAllBytes(...) ``` 查看文件结果看这个路由:  获取statusBackingStore中对应Connector的tasks,结果就在tasks\['trace'\]里,可以自己看看这个的逻辑,这里就不多说了 环境搭建 ---- 下载3.9.0源码包 gradle构建一下,然后运行命令: ```sh ./bin/zookeeper-server-start.sh config/zookeeper.properties ./bin/kafka-server-start.sh config/server.properties ./bin/connect-distributed.sh config/connect-distributed.properties ``` 这里的connect-distributed.sh如果需要用idea调试的话,最好在里面加上debug,用idea的jvm连接 五、修复方式 ------ Standalone模式:修改connect-standalone.properties中的listeners或rest.host.name字段Distributed模式:修改connect-distributed.properties中的listeners或rest.host.name字段使用流量防护设备(如WAF、防火墙)拦截/connectors接口请求中携带敏感文件路径的恶意流量 END --- DIFF一下,一眼就能发现3.9.1对uri进行了校验: ```java // AccessTokenRetrieverFactory.java public static AccessTokenRetriever create(Map<String, ?> configs, Map<String, Object> metadata) { ConfigurationUtils cu = new ConfigurationUtils(configs); cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_TOKEN_ENDPOINT_URL); // 新增校验 // ...原有逻辑 } // VerificationKeyResolverFactory.java public static VerificationKeyResolver create(Map<String, ?> configs) { ConfigurationUtils cu = new ConfigurationUtils(configs); cu.throwIfURLIsNotAllowed(SASL_OAUTHBEARER_JWKS_ENDPOINT_URL); // 新增校验 // ...原有逻辑 } ``` 漏洞点就是这个。好啦,结束。高中生,菜勿喷。
发表于 2025-06-12 16:11:17
阅读 ( 945 )
分类:
开发框架
3 推荐
收藏
0 条评论
请先
登录
后评论
hahaha123
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!