
Runnable proof-of-concept reproducer for CVE-2026-101259, demonstrating error-message disclosure in Apache Camel camel-coap consumers on Spring Boot and Quarkus, plus fix verification.
Runnable proof-of-concept reproducer for the Apache Camel vulnerability where, when the CoAP consumer could
not build its response once the route had completed, CamelCoapResource caught the resulting exception
and sent its message back to the CoAP client as the payload of a 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) |
The consumer converts the final message body to the byte[] payload, and the CamelCoapMaxAge and
CamelCoapETag headers to their types, after the route has returned — and therefore outside the
route's error handler. An onException in the route cannot catch it, because the failure happens in the
consumer, not in the 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 on an affected build (identical on both 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: ...
The message of such a failure names the class of the value being converted, carries the value's string
form, and ends with the class name and message of the underlying cause — so a remote CoAP client read
class and package names, the value's string form, and whatever the cause carries, which in practice can
include paths, host names and connection detail of a backend. The consumer had no option to suppress
this, unlike the HTTP consumers, which offer muteException for that purpose.
Probe 3 is the second half of the issue. The exception raised by a failing route was not sent at all:
the consumer did not check whether the exchange had failed, and answered a route failure with 2.05 Content
and whatever body the message held at that point — a success code for a failed route.
One thing this reproducer had to get right: a body for which no converter exists is not enough.
getBody(byte[].class)returnsnullin that case rather than throwing, and the consumer then answers2.05with an empty payload — no disclosure. The conversion has to fail, not be unavailable, which is why probe 1 uses a stream body that throws part way through a read. The header probe fails the other way, through a value of the wrong shape.
Reaching the disclosure requires a route whose completed response a remote client can make unconvertible, which depends on how the route is written. Nothing here is authenticated, and plain CoAP GETs are all the client sends.
| Property | Value |
|---|---|
| Component | camel-coap (CamelCoapResource) |
| CWE | CWE-209 (Generation of Error Message Containing Sensitive Information) |
| Severity | Medium |
| Attack vector | A CoAP request to a consumer whose completed response cannot be built — a body that fails conversion to byte[], or a CamelCoapMaxAge / CamelCoapETag header that fails conversion |
| Impact | A remote CoAP client reads class and package names, the string form of the value and the underlying cause's message — in practice paths, host names and backend connection detail |
| Affected Versions | From 4.0.0 before 4.18.5, from 4.19.0 before 4.22.2 |
| Fixed Versions | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-25087 |
| Credit | Zhixi "Jace" Sun, independent security researcher |
Advisory: https://camel.apache.org/security/CVE-2026-101259.html
The consumer gains a muteException option — on the coap, coaps, coap+tcp and coaps+tcp endpoints
alike — which defaults to true on all three fixed release lines, so the upgrade is enough on its own
and no configuration change is needed. The consumer still answers 5.00 Internal Server Error, but with an
empty payload, and it now reports the exception through the consumer's exception handler, so muting it
does not hide it from the operator.
It also checks whether the exchange failed, and answers a route failure with 5.00 Internal Server Error
under the same rule, where it previously answered 2.05 Content.
Fixed on main in apache/camel#26994 (2245feac), backported
to camel-4.22.x in #27046 (868d5604) and to camel-4.18.x
in #27048 (aaf5bbf8).
Set camel.version to 4.22.2 and run it again. Verified against 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.
Probe 3 also changes code, from 2.05 Content to 5.00 Internal Server Error — the route failure is now
answered as a failure.
Make sure the response can always be built once the route has completed: convert the body to byte[] or
String as the last step of each route consuming from a coap endpoint (convertBodyTo), give the
CamelCoapMaxAge and CamelCoapETag headers only values of their own types, and add an onException
handler that marks the failure handled and sets the response body explicitly — so that a conversion failure
is raised and replaced inside the route rather than in the consumer.
This repository is published for educational and defensive purposes: to help Apache Camel users understand the vulnerability, verify whether they are affected, and confirm that upgrading resolves it. Do not use this material against systems you do not own or operate.