
Prova de conceito executável que reproduz a CVE-2026-101259, demonstrando a divulgação de mensagens de erro em consumidores camel-coap do Apache Camel em Spring Boot e Quarkus, além da verificação da correção.
Reprodutor de prova de conceito executável para a vulnerabilidade do Apache Camel em que, quando o consumidor CoAP não conseguia
construir sua resposta após a rota ter sido concluída, CamelCoapResource capturava a exceção resultante
e enviava sua mensagem de volta ao cliente CoAP como payload de um 5.00 Internal Server Error:
} catch (Exception e) {
cexchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, e.getMessage());
}
| Runtime | Diretório | Stack |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-coap 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0) |
O consumer converte o corpo da mensagem final para o payload byte[], e os headers CamelCoapMaxAge e
CamelCoapETag para seus tipos, depois que a rota retornou — e, portanto, fora do
error handler da rota. Um onException na rota não consegue capturá-lo, porque a falha ocorre no
consumer, não na rota.
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until [ "$(curl -s http://localhost:8080/exploit/ready)" = "Started" ]; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
Saída em um build afetado (idêntica em ambos os runtimes):
1) Converting the body to the byte[] payload fails once the route has completed
CoAP GET coap://127.0.0.1:5683/reading
response code : 5.00 (INTERNAL_SERVER_ERROR)
response payload : Error during type conversion from type: com.example.FailingBackendStream to the
required type: byte[] with value com.example.FailingBackendStream@5eede7d7 due to
java.io.IOException: connection to prod-db.internal:5432 reset while streaming
sensor_readings for node edge-gateway-07.dc2.internal
2) The CamelCoapMaxAge header cannot be converted to its type
CoAP GET coap://127.0.0.1:5683/reading-maxage
response code : 5.00 (INTERNAL_SERVER_ERROR)
response payload : Error during type conversion from type: java.lang.String to the required type:
java.lang.Long with value not-a-number due to java.lang.NumberFormatException:
For input string: "not-a-number"
3) A route that simply fails
CoAP GET coap://127.0.0.1:5683/reading-failing
response code : 2.05 (CONTENT)
response payload : partially built response
What a remote CoAP client learned from these responses:
the backend host and port, out of the cause's message : true
class names of the cause : true
the source and target types of the conversion : true
the string form of the offending value : true
a failed route answered with a SUCCESS code : true
>>> PROVEN: ...
A mensagem de tal falha nomeia a classe do valor que está sendo convertido, carrega a forma de string
do valor e termina com o nome da classe e a mensagem da causa subjacente — assim, um cliente CoAP remoto lê
nomes de classe e pacote, a forma de string do valor e tudo o que a causa carrega, o que na prática pode
incluir caminhos, nomes de host e detalhes de conexão de um backend. O consumidor não tinha opção de suprimir
isso, diferentemente dos consumidores HTTP, que oferecem muteException para esse propósito.
A sonda 3 é a segunda metade do problema. A exceção levantada por uma rota com falha não era enviada de forma alguma:
o consumidor não verificava se a troca havia falhado e respondia a uma falha de rota com 2.05 Content
e qualquer corpo que a mensagem tivesse naquele momento — um código de sucesso para uma rota com falha.
Uma coisa que este reprodutor precisou acertar: um corpo para o qual não existe conversor não é suficiente.
getBody(byte[].class)retornanullnesse caso em vez de lançar exceção, e o consumidor então responde2.05com um payload vazio — sem divulgação. A conversão precisa falhar, não estar indisponível, e é por isso que a sonda 1 usa um corpo de stream que lança exceção no meio de uma leitura. A sonda de cabeçalho falha do outro modo, por meio de um valor com formato incorreto.
Alcançar a divulgação exige uma rota cuja resposta concluída um cliente remoto possa tornar não conversível, o que depende de como a rota é escrita. Nada aqui é autenticado, e GETs CoAP simples são tudo o que o cliente envia.
| Propriedade | Valor |
|---|---|
| Componente | camel-coap (CamelCoapResource) |
| CWE | CWE-209 (Geração de Mensagem de Erro Contendo Informações Sensíveis) |
| Severidade | Média |
| Vetor de ataque | Uma requisição CoAP a um consumidor cuja resposta concluída não pode ser construída — um corpo que falha na conversão para byte[], ou um cabeçalho CamelCoapMaxAge / CamelCoapETag que falha na conversão |
| Impacto | Um cliente CoAP remoto lê nomes de classe e pacote, a forma de string do valor e a mensagem da causa subjacente — na prática, caminhos, nomes de host e detalhes de conexão do backend |
| Versões Afetadas | Da 4.0.0 anterior à 4.18.5, da 4.19.0 anterior à 4.22.2 |
| Versões Corrigidas | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-25087 |
| Crédito | Zhixi "Jace" Sun, pesquisador de segurança independente |
Advisory: https://camel.apache.org/security/CVE-2026-101259.html
O consumidor ganha uma opção muteException — nos endpoints coap, coaps, coap+tcp e coaps+tcp
igualmente — que tem como padrão true em todas as três linhas de versão corrigidas, então a atualização é suficiente por si só
e nenhuma alteração de configuração é necessária. O consumidor ainda responde 5.00 Internal Server Error, mas com um
payload vazio, e agora relata a exceção por meio do handler de exceções do consumidor, de modo que silenciá-la
não a oculta do operador.
Ele também verifica se a troca falhou e responde a uma falha de rota com 5.00 Internal Server Error
sob a mesma regra, onde anteriormente respondia 2.05 Content.
Corrigido em main em apache/camel#26994 (2245feac), com backport
para camel-4.22.x em #27046 (868d5604) e para camel-4.18.x
em #27048 (aaf5bbf8).
Defina camel.version como 4.22.2 e execute novamente. Verificado contra 4.22.1 → 4.22.2:
1) ... response code: 5.00 (INTERNAL_SERVER_ERROR) response payload : <empty>
2) ... response code: 5.00 (INTERNAL_SERVER_ERROR) response payload : <empty>
3) ... response code: 5.00 (INTERNAL_SERVER_ERROR) response payload : <empty>
>>> NOT reproduced: the error payload was empty.
O Probe 3 também altera o código, de 2.05 Content para 5.00 Internal Server Error — a falha de rota agora é
respondida como uma falha.
Certifique-se de que a resposta possa sempre ser construída após a conclusão da rota: converta o corpo para byte[] ou
String como última etapa de cada rota que consome de um endpoint coap (convertBodyTo), atribua aos cabeçalhos
CamelCoapMaxAge e CamelCoapETag apenas valores de seus próprios tipos, e adicione um manipulador onException
que marque a falha como tratada e defina o corpo da resposta explicitamente — para que uma falha de conversão
seja lançada e substituída dentro da rota, e não no consumidor.