Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

admin 2023年9月7日13:24:18评论14 views字数 6443阅读21分28秒阅读模式

产品介绍

Spring for Apache Kafka (Spring - Kafka)项目将Spring的核心概念应用于基于Kafka的消息传递解决方案的开发。它提供了一个“template”作为发送消息的高级抽象。它还支持带有@KafkaListener注解和“listener container”的消息驱动POJOs 。这些库提倡使用依赖注入和声明式的使用方式。所有这些例子都与Spring框架的JMS支持和Spring AMQP对RabbitMQ的支持有相似之处。

漏洞概述

当Kafka应用服务器允许一个不受信任的源的生产消息的时候,若其能够控制消息的headers,则可能导致一个反序列化漏洞的发生。

受影响版本

  • 2.8.1 to 2.9.10

  • 3.0.0 to 3.0.9

漏洞分析

调试环境

使用idea新建基于maven的java项目,在pom.xml中引入相关依赖包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.14</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.arliya</groupId>
    <artifactId>SpringKafkaTest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>SpringKafkaTest</name>
    <description>SpringKafkaTest</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
<!--            <version>2.9.11</version>-->
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

这里使用spring-boot-starter-parent版本为2.6.14,其依赖的Spring Kafka版本为2.8.11,为存在漏洞版本;

为了对比,修改Spring Kafka的版本为2.9.11,为不受修复版本。

为了能够进行漏洞复现,我们需要编写一部分代码

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

测试代码主要包括Kafka的生产者于消费者的配置,一个生产消息的Web接口控制器,一个消费消息的Service,关于Spring Kafka的使用方式,可以参考该文章:

https://juejin.cn/post/7207411012479074364

两外,还需要搭建Apache Kafka服务器,建议使用Docker一键搭建,具体方式可以参考以下文章:

https://juejin.cn/post/6960820341631352868

漏洞原理

在进行漏洞复现前,需要有一些于Apache Kafka相关的基础知识。

Kafka 是一种分布式的,基于发布 / 订阅的消息系统。Kafka包含一些基本概念,如生产者、消费者

  • 生产者:负责消息的产生,并推送到Kafka服务器

  • 消费者:负责消息的消费

Kafka的一条消息有一些更精细的组件,包括KeyValueHeaders

  • Key:当一条消息被定义了Key之后,Kafka Server通过消息Key的hash,将相同Key的消息映射到同一个分区中。

  • Value:数据

  • Headers:存储消息元数据,headers内容可以自行定义,类似于HTTP协议中的自定义消息头,当然Kafka中也有一些内部的消息头,该漏洞就与消息头有关

关于Kafka的基础概念还有很多,但对于该漏洞来说,能够理解上述概念已经足够了。若需要深入了解Kafka可以参考这篇文章:

https://zhuanlan.zhihu.com/p/74063251

我们还是从官方的通告入手:

https://spring.io/security/cve-2023-34040

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

漏洞描述中提示了一些关键信息:

  • 存在漏洞的版本为3.x.0 <= version <= 3.0.9 2.x.0 <= 2.9.10,也就是说3.0.10版本是修复版本。

  • Spring Kafka Consumer没有配置ErrorHandlingDeserializer的时候存在漏洞

  • checkDeserExWhenKeyNull或者checkDeserExWhenValueNull被设置为True时存在漏洞

  • 当攻击者可以生产消息

  • 当上述三个条件同时满足时可能存在漏洞

  • 攻击发生在程序反序列化Record Headers的时候

  • 使用了ErrorHandlingDeserializer可以避免漏洞,因为它会删除危险的记录头。

首先,我们到官方仓库找到最新发布的漏洞修复版本v3.0.10

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

更新日志提示了可能的漏洞修复记录

https://github.com/spring-projects/spring-kafka/pull/2770/files

另外,在ErrorHandlingDeserializer中找到其反序列化过程中会删除的Record headers

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

由此可知会被删除的headerspringDeserializerExceptionKeyspringDeserializerExceptionValue,这也意味着漏洞可能就发生在这两个header的值中。所以,此时,我们需要在生产消息的时候为消息增加这两个消息头。

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

这里模拟了用户发送HTTP请求设置消息的情况

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

在前面提到的漏洞存在的条件中,提到需要设置checkDeserExWhenKeyNull  或者checkDeserExWhenValueNullTrue,我们首先找到这两个变量在ConsumerProperties类中。顾名思义,这是一个与消费者相关的配置。

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

这两个配置需要在创建消费者容器工厂的时候配置为True,现在我们的漏洞复现环境才算完整。

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

我们到上面提到的commit记录中找到一个新增的测试类

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

这个测试类的功能大概是模拟了一条消息,其携带了Value类型的反序列化异常消息头,即:springDeserializerExceptionValue

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

然后通过调用SerializationUtils.getExceptionFromHeader方法查看是否能够返回一个Exception对象,若不能则测试通过。同时我们注意到该commit很多地方将ListenerUtils.getExceptionFromHeader方法替换为SerializationUtils.getExceptionFromHeader方法

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

那么证明SerializationUtils.getExceptionFromHeader方法中存在漏洞的修复,而按照原始的逻辑执行代码会导致漏洞。

那么我们将断点打在ListenerUtils.getExceptionFromHeader处进行调试:

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

这个时候可以通过IDEA查看以下程序的调用栈

checkDeser:2766, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
invokeOnMessage:2648, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
doInvokeRecordListener:2577, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
doInvokeWithRecords:2457, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
invokeRecordListener:2335, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
invokeListener:2006, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
invokeIfHaveRecords:1375, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
pollAndInvoke:1366, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
run:1257, KafkaMessageListenerContainer$ListenerConsumer (org.springframework.kafka.listener)
call:511, Executors$RunnableAdapter (java.util.concurrent)
run$$$capture:266, FutureTask (java.util.concurrent)
run:-1, FutureTask (java.util.concurrent)

在第8行的代码逻辑中有两个重要的地方,在doPoll方法中会完成消息的反序列化得到records对象,这中间需要保证消息拥有正确的数据格式以避免发生异常导致程序不能运行到第1366invokeIfHaveRecords方法处,所谓invokeIfHaveRecords即在消费者成功获取到消息并反序列化后调用程序中设置的监听器回调进行跟进一步的处理,即被KafkaListener注解修饰的逻辑的处理逻辑,不过在进行这步操作前包含一些必要的检测,如:记录Headers元数据的检查。

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

回到ListenerUtils.getExceptionFromHeader的调用逻辑。该方法首先在104行处获取到记录headers能够与headerName 变量值springDeserializerExceptionKey匹配的头信息。然后将该标头的值取出来调用byteArrayToDeserializationException方法

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

byteArrayToDeserializationException方法中有典型的反序列化逻辑,不过因为该部分代码重写了resolveClass方法限制了反序列化目标类的全类名必须为org.springframework.kafka.support.serializer.DeserializationException 所以不能进行任意类的反序列化。

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

DeserializationException含有一个有参构造方法,其接受4个参数分别表示异常提示信息、异常数据、是否是key反序列化异常以及导致异常的原因

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

第四个参数是一个对象,其类型是一个顶级的异常接口,如果存在一个其实现类含有可被利用的代码,便可进行反序列化漏洞利用。这里我们自行编写了一个异常类,该异常类在被加载的时候便会执行静态代码块中的代码。

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

使用以下代码生成序列化数据

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

将该数据通过send接口发送给kafka服务器

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

成功RCE

Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

可以注意到,该漏洞利用限制很多。很多配置都并不是常用配置,所以讲到危害其实是有限的。反序列化的目标对象会在第一次被发序列化后被缓存下来,所以复现时可能会出现第一次成功,后面测试不成功的现象。

另外,此处我们用的是自定义的异常类,那么在生产环境中,这类Gadgets怎么找呢?其实可以参考下FastJSON V1.2.80那个反序列化漏洞,其利用的Gadgets就是一些实现了Throwable接口的异常类。

修复措施

  1. 升级软件到不受影响的版本或最新版,官方仓库地址:https://github.com/spring-projects/spring-kafka/releases

  2. 规范编码,避免不受信任用户编辑消息元数据;

  3. 配置ErrorHandlingDeserializer进行异常处理。

参考链接

  • https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-34040

  • https://www.cvedetails.com/cve-details.php?t=1&cve_id=CVE-2023-34040

  • https://www.cnnvd.org.cn/home/globalSearch?keyword=CVE-2023-34040

  • https://spring.io/security/cve-2023-34040

  • https://github.com/spring-projects/spring-kafka/pull/2770/files

原文始发于微信公众号(一个安全研究员):漏洞复现 | Spring Kafka消息头反序列化漏洞(CVE-2023-34040)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年9月7日13:24:18
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Spring Kafka消息头反序列化漏洞(CVE-2023-34040)https://cn-sec.com/archives/2016575.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息