
Prueba de concepto ejecutable en Docker para CVE-2026-71569, una omisión de la protección SSRF en la validación de webhooks de Apache Camel camel-a2a, con sondas y verificación de la corrección.
Reproductor de prueba de concepto ejecutable para la vulnerabilidad de Apache Camel en la que WebhookUrlValidator
clasificaba un host de webhook de notificación push en dos lugares que no coincidían:
fc00::/7 ("fc", "fd");isLoopbackAddress,
isAnyLocalAddress, isLinkLocalAddress e isSiteLocalAddress.Esos predicados no cubren el mismo terreno: isSiteLocalAddress informa del bloque obsoleto fec0::/10,
no de las direcciones locales únicas fc00::/7 que lo reemplazaron, y el JDK no incluye ningún predicado para
ellas en absoluto. La misma dirección, por lo tanto, se aceptaba o se rechazaba según cómo estuviera escrita.
| Runtime | Directorio | Stack |
|---|---|---|
| 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) |
El nombre de host tiene que resolverse dentro de fc00::/7, lo que el archivo compose consigue con una entrada extra_hosts
(Docker la escribe en el /etc/hosts del contenedor al arrancar) — así que ejecuta esto a través de Docker, no con un
java -jar directo:
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
En una compilación afectada (idéntico en ambos runtimes):
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: ...
Las sondas 1 y 2 son la misma dirección, y los veredictos no coinciden. La sonda 3 es la misma inconsistencia en la
dirección opuesta: la comprobación previa de la cadena coincidió con el host sin procesar sin establecer primero que fuera un literal
IP, por lo que cualquier nombre que simplemente comenzara por fc o fd era rechazado de plano — fcm.googleapis.com, un
endpoint de push perfectamente ordinario, entre ellos. Las sondas 4 y 5 son controles que muestran la protección funcionando donde
fue escrita correctamente.
Todas las sondas pasan allowLocal=false, que es el valor predeterminado (allowLocalWebhookUrls=false) y la
configuración en la que se espera que la protección se mantenga.
La URL del webhook llega como TaskPushNotificationConfig.url desde el llamador que registra la notificación
push, por lo que está controlada por el atacante. PushNotificationDispatcher entonces envía la notificación
push a esa dirección, junto con cualquier cabecera Authorization transportada en la misma configuración —
cuyo esquema y credenciales proporciona el llamador que registra.
La respuesta no se retransmite de vuelta a ese llamador, por lo que lo que esto produce es una solicitud HTTPS
saliente ciega desde el proceso de Camel hacia una dirección interna que la protección fue escrita para rechazar. Alcanzar un servicio
de esa manera requiere además que el nombre resuelva en fc00::/7 en el entorno objetivo, que un
servicio HTTPS esté escuchando en esa dirección, y que el handshake TLS tenga éxito para el nombre que se
solicita — razón por la cual la severidad se evalúa como media.
| Propiedad | Valor |
|---|---|
| Componente | camel-a2a (WebhookUrlValidator) |
| CWE | CWE-918 (Server-Side Request Forgery) |
| Severidad | Media |
| Vector de ataque | Una URL de webhook de notificación push cuyo nombre de host resuelve en fc00::/7 |
| Impacto | Una solicitud HTTPS saliente ciega, con la cabecera Authorization configurada, hacia una dirección interna que la protección debería haber rechazado |
| Versiones afectadas | Desde 4.21.0 antes de 4.22.2 |
| Versiones corregidas | 4.22.2, 4.23.0 |
| JIRA | CAMEL-24371 |
| Crédito | 2122295973 at qq dot com, reportado a través del ASF Security Team |
Aviso: https://camel.apache.org/security/CVE-2026-71569.html
Ambas rutas ahora pasan por InetAddress.getByName, que analiza un literal IPv6 entre corchetes sin tocar
DNS, y clasifican la dirección resultante a partir de sus bytes sin procesar con un clasificador compartido — de modo que un host se
juzga por la dirección que denota en lugar de por cómo se escribió. Ese clasificador reconoce fc00::/7
y el espacio de direcciones compartido 100.64.0.0/10, así como direcciones IPv4-compatibles, NAT64 (64:ff9b::/96) y 6to4
(2002::/16) cuya dirección IPv4 incrustada no es enrutable globalmente.
Corregido en main en apache/camel#25406 (54041cce), retroportado
a camel-4.22.x en #26583 (c4a5c5fc).
Establezca camel.version en 4.22.2 y ejecútelo de nuevo. Verificado contra 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
Ambas grafías ahora obtienen el mismo veredicto, con el mismo mensaje, y el falso positivo en la sonda 3 también ha desaparecido.
Coloque autenticación delante de la ruta de configuración de notificaciones push de A2A para que la URL del webhook no llegue desde un llamador no confiable, y deniegue la salida del proceso de Camel hacia fc00::/7 a nivel de red. Cuando el despliegue no necesite IPv6, iniciar la JVM con -Djava.net.preferIPv4Stack=true elimina la clase de dirección sobre la que se basa la brecha. Tenga en cuenta que allowLocalWebhookUrls=false es ya el valor predeterminado y no cierra la brecha por sí solo — la sonda 2 anterior se ejecuta con él.
Este repositorio se publica con fines educativos y defensivos: para ayudar a los usuarios de Apache Camel a comprender la vulnerabilidad, verificar si se ven afectados y confirmar que la actualización la resuelve. No utilice este material contra sistemas que no sean de su propiedad o que no opere.