Contrast-Security-OSS/Spring-Kafka-POC-CVE-2023-34040

GitHub: Contrast-Security-OSS/Spring-Kafka-POC-CVE-2023-34040

一个用于复现 Spring-Kafka 反序列化漏洞 CVE-2023-34040 的概念验证工具,支持 RCE 和 DoS 两种攻击载荷。

Stars: 46 | Forks: 6

# Spring Kafka 反序列化 POC ## 环境要求 * Java 11 * Maven * Docker(或 Kafka) ## 使用方法 首先启动 Docker 化的 Kafka 实例。这将启动 Kafka 并使其在 29092 端口可用。 ``` docker-compose up ``` 启动 Consumer 应用程序。 ``` cd spring-kafka-consumer mvn clean install mvn spring-boot:run ``` 这将构建并启动 Consumer 应用程序。Consumer 最多会等待 10 分钟以接收消息,超时后关闭。 Producer ``` cd spring-kafka-producer mvn clean install mvn spring-boot:run ``` 这将构建并启动 Producer 应用程序。Producer 会向 Kafka 队列发送一条消息后关闭。 ### Spring-Kafka-Consumer 配置 要使此漏洞利用成功,需要在 Consumer 上启用以下一个或两个标志: `CheckDeserExWhenValueNull` `CheckDeserExWhenKeyNull` 在 Consumer 应用程序中的 `KafkaConsumerConfig.greetingKafkaListenerContainerFactory()` 方法中进行配置。 ### 载荷 有两种可触发的载荷,默认使用 RCE 载荷。 #### 拒绝服务(Denial of Service) 要启用 DOS 载荷,修改 `KafkaApplication.sendGreetingMessage()` 方法。 将作为 header 添加的载荷改为 `dosPayload`,然后重新构建并运行 Producer。 注意:由于 DOS 在消息读取时发生,且读取永远不会完成,消息会一直保留在队列中,直到手动删除或消息保留时间到期。这增加了 DOS 的效力,因为在手动干预或保留时间到期之前,该队列将不可用。 这可能导致紧随 DOS 消息之后发送的消息丢失。 #### RCE 要启用 RCE 载荷,修改 `KafkaApplication.sendGreetingMessage()` 方法。 将作为 header 添加的载荷改为 `rcePayload`,然后重新构建并运行 Producer。默认执行的命令是 `touch /tmp/newfile`,要验证攻击是否成功,请检查 /tmp 下是否存在名为 newfile 的文件。 如果在 Windows 上运行,可以将命令字符串修改为更适合的命令。 这个 Gadget 本质上只是一个 POC。要在真实世界中实现 RCE,Consumer 的类路径上需要有可用的 gadget 类。 #### 工作原理 拒绝服务不需要 Consumer 的类路径上有任何特定的 gadget 类。它依赖于生成一个修改过的类版本: `org.springframework.kafka.support.serializer.DeserializationException`,其中包含一个 Object。 这使得我们可以轻松地将任意载荷添加到序列化对象中。 这个修改后的类名为 `xrg.springframework.kafka.support.serializer.DeserializationException`(注意包名开头的 x)。 注入载荷后,在本例中是一个使用 `java.util.Set` 和 `java.lang.Object` 的 billion laughs 风格攻击,然后进行序列化。 接着修改二进制数据,将 x 改为 o,以匹配 Consumer 所期望的类名。 这个序列化后的异常类随后被添加到消息 header 中,分别放入 `springDeserializerExceptionValue` header 和 `springDeserializerExceptionKey` header。 如果 key 或 message 为 null,Consumer 会读取这些 header。只需确保 key 或 message 为 null,Consumer 就会读取它。 Spring-Kafka 内部存在一些反序列化保护机制,位于 ListenerUtils 中。 ``` public static DeserializationException byteArrayToDeserializationException(LogAccessor logger, byte[] value) { try { ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(value)) { boolean first = true; @Override protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { if (this.first) { this.first = false; Assert.state(desc.getName().equals(DeserializationException.class.getName()), "Header does not contain a DeserializationException"); } return super.resolveClass(desc); } }; return (DeserializationException) ois.readObject(); } catch (IOException | ClassNotFoundException | ClassCastException e) { logger.error(e, "Failed to deserialize a deserialization exception"); return null; } } ``` 可以看到有一个检查,确保顶层类是 `org.springframework.kafka.support.serializer.DeserializationException`。但注意只检查了顶层类,而且只检查了类名(攻击者有能力修改该名称)。因此该层级以下的任何载荷都会被反序列化。
标签:CVE-2023-34040, Docker, DoS, JS文件枚举, Kafka消费者, Maven, Maven, meg, POC, Proof of Concept, RCE, Spring Kafka, Spring框架, 信息安全, 反序列化漏洞, 域名枚举, 安全检查绕过, 安全防御评估, 序列化攻击, 拒绝服务, 攻击技术, 消息队列, 漏洞验证, 漏洞验证, 编程工具, 网络安全, 请求拦截, 远程代码执行, 隐私保护