
Работающий Docker proof-of-concept для CVE-2026-71569 — обхода защиты от SSRF при валидации вебхуков в Apache Camel camel-a2a, с проверками и верификацией исправления.
Запускаемый воспроизводитель proof-of-concept для уязвимости Apache Camel, при которой WebhookUrlValidator
классифицировал хост push-уведомления webhook в двух местах, которые не согласовывались между собой:
fc00::/7 ("fc", "fd");isLoopbackAddress,
isAnyLocalAddress, isLinkLocalAddress и isSiteLocalAddress.Эти предикаты не покрывают одну и ту же область: isSiteLocalAddress сообщает об устаревшем блоке fec0::/10,
а не об уникальных локальных адресах fc00::/7, которые его заменили, и в JDK для них вообще нет
предиката. Таким образом, один и тот же адрес принимался или отклонялся в зависимости от того, как он был записан.
| Среда выполнения | Каталог | Стек |
|---|---|---|
| Camel Spring Boot | camel-spring-boot/ | Spring Boot 3.5.13 + camel-a2a 4.22.1 |
| Camel Quarkus | camel-quarkus/ | Camel Quarkus 3.39.0 (Quarkus 3.39.0, Camel 4.22.0) |
Имя хоста должно разрешаться в fc00::/7, что compose-файл обеспечивает записью extra_hosts
(Docker записывает её в /etc/hosts контейнера при запуске) — поэтому запускайте это через Docker, а не с
помощью простого java -jar:
cd camel-spring-boot # or: cd camel-quarkus
mvn clean package
docker compose up -d --build
until curl -sf http://localhost:8080/exploit/ready >/dev/null; do sleep 1; done
curl -s http://localhost:8080/exploit/attack
docker compose down
Вывод на уязвимой сборке (идентичен в обеих средах выполнения):
Resolution in this environment: internal-service.example -> fd00:0:0:0:0:0:0:1
1) The unique local address as an IP LITERAL
url: https://[fd00::1]/push-notification
verdict: REFUSED - Webhook URL must not point to private/internal IPv6 ranges (SSRF protection): [fd00::1]
2) The SAME address reached through a host name
url: https://internal-service.example/push-notification
verdict: ACCEPTED, resolved to fd00:0:0:0:0:0:0:1
3) A legitimate host name that merely BEGINS with "fc" (the bug's other direction)
url: https://fcm.googleapis.com/push-notification
verdict: REFUSED - Webhook URL must not point to private/internal IPv6 ranges (SSRF protection): fcm.googleapis.com
4) CONTROL - an IPv4 private address, which isSiteLocalAddress does cover
verdict: REFUSED - ... site-local/private address ...
5) CONTROL - a link-local IPv6 literal
verdict: REFUSED - ... private/internal IPv6 ranges ...
>>> PROVEN: ...
Пробы 1 и 2 — один и тот же адрес, и вердикты расходятся. Проба 3 — та же несогласованность в
обратном направлении: предварительная проверка строки совпала с необработанным хостом без предварительного
установления того, что это IP-литерал, поэтому любое имя, лишь начинающееся с fc или fd, отклонялось
сразу — fcm.googleapis.com, вполне обычная конечная точка push-уведомлений, в том числе. Пробы 4 и 5 —
контрольные, показывающие, что защита работает там, где она была написана правильно.
Каждая проба передаёт allowLocal=false, что является значением по умолчанию (allowLocalWebhookUrls=false) и
конфигурацией, в которой защита, как ожидается, должна срабатывать.
URL вебхука поступает как TaskPushNotificationConfig.url от вызывающей стороны, регистрирующей push-уведомление,
поэтому он контролируется атакующим. Затем PushNotificationDispatcher отправляет push-уведомление на этот
адрес вместе с любым заголовком Authorization, содержащимся в той же конфигурации, — схему и учётные данные
для которого предоставляет регистрирующаяся вызывающая сторона.
Ответ не передаётся обратно этой вызывающей стороне, поэтому результатом является слепой исходящий
HTTPS-запрос от процесса Camel к внутреннему адресу, который защита должна была отклонить. Для достижения
сервиса таким способом дополнительно требуется, чтобы имя разрешалось в fc00::/7 в целевой среде, чтобы на
этом адресе прослушивался HTTPS-сервис и чтобы TLS-рукопожатие прошло успешно для запрашиваемого имени — поэтому
уровень серьёзности оценивается как средний.
| Свойство | Значение |
|---|---|
| Компонент | camel-a2a (WebhookUrlValidator) |
| CWE | CWE-918 (Server-Side Request Forgery) |
| Серьёзность | Средняя |
| Вектор атаки | URL вебхука push-уведомлений, имя хоста которого разрешается в fc00::/7 |
| Воздействие | Слепой исходящий HTTPS-запрос с настроенным заголовком Authorization к внутреннему адресу, который защита должна была отклонить |
| Затронутые версии | С 4.21.0 до 4.22.2 |
| Исправленные версии | 4.22.2, 4.23.0 |
| JIRA | CAMEL-24371 |
| Благодарность | 2122295973 at qq dot com, сообщено через ASF Security Team |
Рекомендация: https://camel.apache.org/security/CVE-2026-71569.html
Оба пути теперь проходят через InetAddress.getByName, который разбирает IPv6-литерал в скобках без
обращения к DNS, и классифицируют полученный адрес по его сырым байтам с помощью одного общего
классификатора — так хост оценивается по адресу, который он обозначает, а не по тому, как он был записан. Этот
классификатор распознаёт fc00::/7 и общее адресное пространство 100.64.0.0/10, а также IPv4-совместимые,
NAT64 (64:ff9b::/96) и 6to4 (2002::/16) адреса, встроенный IPv4-адрес которых не является глобально
маршрутизируемым.
Исправлено в main в apache/camel#25406 (54041cce),
перенесено в camel-4.22.x в #26583 (c4a5c5fc).
Установите camel.version в 4.22.2 и запустите снова. Проверено на 4.22.1 → 4.22.2:
1) The unique local address as an IP LITERAL
verdict: REFUSED - Webhook URL must not point to a unique local address (SSRF protection): [fd00::1]
2) The SAME address reached through a host name
verdict: REFUSED - Webhook URL must not point to a unique local address (SSRF protection): internal-service.example
3) A legitimate host name that merely BEGINS with "fc"
verdict: ACCEPTED, resolved to 216.239.38.55
Оба варианта написания теперь получают одинаковый вердикт с одинаковым сообщением, и ложноположительный результат в проверке 3 тоже исчез.
Разместите аутентификацию перед путём конфигурации push-уведомлений A2A, чтобы URL вебхука не поступал от недоверенного вызывающего, и запретите исходящий трафик из процесса Camel к fc00::/7 на сетевом уровне. Если в развёртывании нет необходимости в IPv6, запуск JVM с -Djava.net.preferIPv4Stack=true устраняет класс адресов, на котором основана уязвимость. Обратите внимание, что allowLocalWebhookUrls=false уже является значением по умолчанию и сам по себе не закрывает уязвимость — проверка 2 выше выполняется с ним.
Этот репозиторий опубликован в образовательных и защитных целях: чтобы помочь пользователям Apache Camel понять уязвимость, проверить, затронуты ли они, и убедиться, что обновление её устраняет. Не используйте этот материал против систем, которыми вы не владеете и которые не эксплуатируете.