
Рабочий воспроизводитель proof-of-concept для CVE-2026-101259, демонстрирующий раскрытие сообщений об ошибках в потребителях camel-coap Apache Camel на Spring Boot и Quarkus, а также проверку исправления.
Запускаемый воспроизводитель proof-of-concept для уязвимости Apache Camel, при которой, когда CoAP-консьюмер не мог
построить свой ответ после завершения маршрута, CamelCoapResource перехватывал возникшее исключение
и отправлял его сообщение обратно CoAP-клиенту в качестве полезной нагрузки 5.00 Internal Server Error:
} catch (Exception e) {
cexchange.respond(ResponseCode.INTERNAL_SERVER_ERROR, e.getMessage());
}
| Среда выполнения | Каталог | Стек |
|---|---|---|
| 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) |
Consumer преобразует итоговое тело сообщения в полезную нагрузку byte[], а заголовки CamelCoapMaxAge и
CamelCoapETag — в соответствующие типы, после того как маршрут вернул управление, и, следовательно,
вне обработчика ошибок маршрута. onException в маршруте не может перехватить эту ошибку, поскольку сбой
происходит в consumer, а не в маршруте.
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
Вывод на уязвимой сборке (идентичен в обеих средах выполнения):
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: ...
Сообщение о такой ошибке называет класс значения, которое преобразуется, содержит строковую
форму значения и заканчивается именем класса и сообщением нижележащей причины — так удалённый клиент CoAP
прочитал имена классов и пакетов, строковую форму значения и всё, что несёт причина, что на практике может
включать пути, имена хостов и сведения о подключении к бэкенду. У потребителя не было возможности подавить
это, в отличие от HTTP-потребителей, которые для этой цели предлагают muteException.
Проба 3 — вторая половина проблемы. Исключение, вызванное сбойным маршрутом, вообще не отправлялось:
потребитель не проверял, завершился ли обмен неудачей, и отвечал на сбой маршрута кодом 2.05 Content
и тем телом, которое сообщение содержало на тот момент, — кодом успеха для сбойного маршрута.
Одна вещь, которую этот воспроизводитель должен был учесть: тела, для которого не существует конвертера, недостаточно.
getBody(byte[].class)в этом случае возвращаетnull, а не выбрасывает исключение, и потребитель затем отвечает2.05с пустой полезной нагрузкой — без раскрытия. Преобразование должно завершиться неудачей, а не быть недоступным, поэтому проба 1 использует потоковое тело, которое выбрасывает исключение посреди чтения. Проба с заголовком сбоит иначе — через значение неправильной формы.
Для достижения раскрытия нужен маршрут, завершённый ответ которого удалённый клиент может сделать непреобразуемым, что зависит от того, как написан маршрут. Здесь ничего не аутентифицируется, и всё, что отправляет клиент, — это обычные GET-запросы CoAP.
| Свойство | Значение |
|---|---|
| Компонент | camel-coap (CamelCoapResource) |
| CWE | CWE-209 (Генерация сообщения об ошибке, содержащего конфиденциальную информацию) |
| Критичность | Средняя |
| Вектор атаки | CoAP-запрос к потребителю, завершённый ответ которого невозможно построить — тело, которое не удаётся преобразовать в byte[], или заголовок CamelCoapMaxAge / CamelCoapETag, который не удаётся преобразовать |
| Воздействие | Удалённый клиент CoAP читает имена классов и пакетов, строковую форму значения и сообщение нижележащей причины — на практике пути, имена хостов и сведения о подключении к бэкенду |
| Затронутые версии | С 4.0.0 до 4.18.5, с 4.19.0 до 4.22.2 |
| Исправленные версии | 4.18.5, 4.22.2, 4.23.0 |
| JIRA | CAMEL-25087 |
| Благодарность | Zhixi "Jace" Sun, независимый исследователь безопасности |
Рекомендация: https://camel.apache.org/security/CVE-2026-101259.html
У потребителя появляется опция muteException — одинаково на эндпоинтах coap, coaps, coap+tcp и coaps+tcp —
которая по умолчанию равна true во всех трёх исправленных ветках выпуска, поэтому обновления достаточно самого по себе
и никаких изменений конфигурации не требуется. Потребитель по-прежнему отвечает 5.00 Internal Server Error, но с
пустой полезной нагрузкой, и теперь сообщает об исключении через обработчик исключений потребителя, так что его
подавление не скрывает его от оператора.
Он также проверяет, завершился ли обмен неудачей, и отвечает на сбой маршрута кодом 5.00 Internal Server Error
по тому же правилу, тогда как раньше отвечал 2.05 Content.
Исправлено в main в apache/camel#26994 (2245feac), перенесено
в camel-4.22.x в #27046 (868d5604) и в camel-4.18.x
в #27048 (aaf5bbf8).
Установите camel.version в 4.22.2 и запустите снова. Проверено на 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 также изменяет код ответа с 2.05 Content на 5.00 Internal Server Error — сбой маршрута теперь
возвращается как ошибка.
Убедитесь, что ответ всегда может быть сформирован после завершения маршрута: преобразуйте тело в byte[] или
String на последнем шаге каждого маршрута, потребляющего данные из coap-эндпоинта (convertBodyTo), задавайте
заголовкам CamelCoapMaxAge и CamelCoapETag только значения их собственных типов и добавьте обработчик
onException, который помечает сбой как обработанный и явно задаёт тело ответа — чтобы сбой преобразования
возникал и заменялся внутри маршрута, а не в consumer.