
Riproduttore proof-of-concept eseguibile per CVE-2026-101259, che dimostra la divulgazione dei messaggi di errore nei consumer camel-coap di Apache Camel su Spring Boot e Quarkus, oltre alla verifica della correzione.
Proof-of-concept riproducibile ed eseguibile per la vulnerabilità di Apache Camel in cui, quando il consumer CoAP non
riusciva a costruire la propria risposta una volta completata la route, CamelCoapResource intercettava l'eccezione risultante
e ne inviava il messaggio al client CoAP come payload di un 5.00 Internal Server Error:
} catch (Exception e) {
cexchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, e.getMessage());
}
| Runtime | Directory | 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) |
Il consumer converte il corpo finale del messaggio nel payload byte[], e gli header CamelCoapMaxAge e
CamelCoapETag nei loro tipi, dopo che la route è ritornata — e quindi al di fuori del
gestore degli errori della route. Un onException nella route non può intercettarlo, perché il fallimento avviene nel
consumer, non nella route.
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
Output su una build interessata (identico su entrambi i runtime):
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: ...
Il messaggio di un tale errore riporta il nome della classe del valore in fase di conversione, contiene la forma stringa del valore e termina con il nome della classe e il messaggio della causa sottostante — così un client CoAP remoto leggeva nomi di classi e package, la forma stringa del valore e tutto ciò che la causa trasporta, che in pratica può includere percorsi, nomi host e dettagli di connessione di un backend. Il consumer non aveva alcuna opzione per sopprimere questo, a differenza dei consumer HTTP, che offrono muteException a tale scopo.
La sonda 3 è la seconda metà del problema. L'eccezione sollevata da una route in errore non veniva affatto inviata: il consumer non verificava se lo scambio fosse fallito e rispondeva a un errore di route con 2.05 Content e con qualunque corpo il messaggio contenesse in quel momento — un codice di successo per una route fallita.
Una cosa che questo riproduttore doveva azzeccare: un corpo per cui non esiste alcun converter non è sufficiente.
getBody(byte[].class)restituiscenullin quel caso invece di sollevare un'eccezione, e il consumer risponde poi2.05con un payload vuoto — nessuna divulgazione. La conversione deve fallire, non essere indisponibile, ed è per questo che la sonda 1 usa un corpo stream che solleva un'eccezione a metà di una lettura. La sonda sull'header fallisce nell'altro modo, tramite un valore dalla forma errata.
Raggiungere la divulgazione richiede una route la cui risposta completata possa essere resa non convertibile da un client remoto, il che dipende da come è scritta la route. Nulla qui è autenticato, e le GET CoAP semplici sono tutto ciò che il client invia.
| Proprietà | Valore |
|---|---|
| Componente | camel-coap (CamelCoapResource) |
| CWE | CWE-209 (Generation of Error Message Containing Sensitive Information) |
| Gravità | Media |
| Vettore di attacco | Una richiesta CoAP a un consumer la cui risposta completata non può essere costruita — un corpo che fallisce la conversione a byte[], oppure un header CamelCoapMaxAge / CamelCoapETag che fallisce la conversione |
| Impatto | Un client CoAP remoto legge nomi di classi e package, la forma stringa del valore e il messaggio della causa sottostante — in pratica percorsi, nomi host e dettagli di connessione del backend |
| Versioni interessate | Dalla 4.0.0 precedente alla 4.18.5, dalla 4.19.0 precedente alla 4.22.2 |
| Versioni corrette | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-25087 |
| Credito | Zhixi "Jace" Sun, ricercatore indipendente di sicurezza |
Advisory: https://camel.apache.org/security/CVE-2026-101259.html
Il consumer acquisisce un'opzione muteException — sugli endpoint coap, coaps, coap+tcp e coaps+tcp
allo stesso modo — che ha valore predefinito true su tutte e tre le linee di rilascio corrette, quindi l'aggiornamento è sufficiente da solo
e non è necessaria alcuna modifica alla configurazione. Il consumer risponde comunque 5.00 Internal Server Error, ma con un
payload vuoto, e ora segnala l'eccezione tramite l'exception handler del consumer, così silenziarla
non la nasconde all'operatore.
Inoltre verifica se lo scambio è fallito e risponde a un errore di route con 5.00 Internal Server Error
secondo la stessa regola, dove prima rispondeva 2.05 Content.
Corretto su main in apache/camel#26994 (2245feac), backportato
su camel-4.22.x in #27046 (868d5604) e su camel-4.18.x
in #27048 (aaf5bbf8).
Imposta camel.version su 4.22.2 ed eseguilo di nuovo. Verificato su 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.
Anche la sonda 3 cambia codice, da 2.05 Content a 5.00 Internal Server Error — il fallimento della route viene ora
restituito come un errore.