flawopen.com/AI Security/mlflow-webhook-ssrf-cve-2026-64849
CVE-2026-64849: SSRF em webhooks do MLflow
CVE-2026-64849 é uma falsificação de requisição do lado do servidor (SSRF) sem autenticação na entrega de webhooks do MLflow (alta, CVSS 3.1: 8.6). A validação da URL inicial não restringia os destinos de redirecionamento nem o endereço usado pela conexão real; o endpoint de teste podia devolver o corpo da resposta upstream.[1]
Imagine uma recepção que confere o endereço escrito no pacote, mas deixa o entregador seguir uma instrução de encaminhamento sem verificar o novo destino. O MLflow verificava a URL do webhook antes da conexão, mas redirecionamentos e uma nova consulta DNS podiam levar a requisição a outro lugar. A correção verifica o endereço do peer realmente conectado antes de enviar dados HTTP.[1], [2]
Conceitos Centrais e Termos
SSRF- O servidor faz uma requisição de rede escolhida pelo solicitante e cruza a fronteira de confiança da rede.[1]
Endpoint de teste de webhook- API do MLflow que envia um evento de teste ao webhook configurado; em servidores padrão afetados, o corpo upstream era retornado ao solicitante.[1]
Mapeamentos de frameworks
- Referência CWE para a fraqueza descrita neste guia: CWE-918
- D3-OTF Filtragem de tráfego de saída — A restrição de rede de saída deste guia limita os destinos que o serviço ou host pode alcançar.
- CISA KEV — adicionado em 2026-08-19 — CVE-2026-64849
Fluxo de Ataque Passo a Paso
Um servidor MLflow acessível aceita o teste de webhook
O alerta descreve um Tracking Server padrão sem autenticação. Plugins de autenticação e controles de implantação podem alterar o alcance; confirme a configuração real. A CISA adicionou o CVE ao catálogo Known Exploited Vulnerabilities em 19 de agosto de 2026; a inclusão indica exploração conhecida, mas não comprova o comprometimento de uma implantação específica.[1], [4], [5]
A URL inicial passa pela validação
_validate_webhook_url resolve e verifica o hostname original, mas descarta o endereço verificado em vez de vinculá-lo ao socket posterior.[1]
Um redirecionamento ou nova resolução chega a outro destino
O cliente HTTP segue redirecionamentos e a conexão pode resolver o hostname novamente. A resposta do teste refletia o corpo upstream, permitindo SSRF de leitura completa.[1]
A correção upstream verifica o peer conectado
O PR #24258 valida o peer do socket logo após a conexão e antes dos dados TLS/HTTP; a sessão também evita o roteamento por proxy do ambiente. MLflow 3.15.0 corrige a CVE; falhas IPv6 distintas foram corrigidas em 3.16.0. O aviso credita freeman-bb como denunciante privado original e AUTHENSOR como pesquisador independente.[1], [2], [3], [6]
Código-Fonte: Vulnerável vs. Seguro
Os trechos mostram a fronteira entre validação da URL e a requisição original, além da verificação upstream do peer no momento da conexão. Não incluem requisições de exploração nem destinos internos. A normalização IPv6 do 3.16.0 é uma melhoria posterior e separada.[2]
# Trecho de mlflow/webhooks/delivery.py antes do PR #24258.
_validate_webhook_url(webhook.url) # checks the initial URL only
return session.post(
webhook.url, data=payload_bytes, headers=headers, timeout=timeout
) # redirects follow by default; the validated IP is not pinned
# Trecho da correção upstream que verifica o peer no momento da conexão.
def _assert_public_peer(sock):
if _MLFLOW_WEBHOOK_ALLOW_PRIVATE_IPS.get():
return
try:
peer_ip = sock.getpeername()[0]
except OSError as exc:
sock.close()
raise SSRFProtectionError(
f"Could not determine webhook connection peer address: {exc}"
) from exc
try:
ip = ipaddress.ip_address(peer_ip)
except ValueError as exc:
sock.close()
raise SSRFProtectionError(
f"Webhook connection resolved to an invalid IP address: {peer_ip!r}"
) from exc
if not ip.is_global:
sock.close()
raise SSRFProtectionError(
f"Webhook connection blocked: {ip} is not a public IP address. "
"This may indicate a DNS rebinding attempt."
)
class _SSRFProtectedHTTPConnection(HTTPConnection):
def _new_conn(self):
sock = super()._new_conn()
_assert_public_peer(sock)
return sock
class _SSRFProtectedHTTPSConnection(HTTPSConnection):
def _new_conn(self):
sock = super()._new_conn()
_assert_public_peer(sock)
return sock
adapter = SSRFProtectedHTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.trust_env = False
session.mount("http://", adapter)
session.mount("https://", adapter)
Lista de Verificação de Segurança para Engenharia
- ✓Atualize MLflow 3.10.0–3.14.x para pelo menos 3.15.0 para corrigir
CVE-2026-64849; prefira uma versão suportada atual. Use 3.16.0 ou posterior onde mecanismos de transição IPv6 possam existir.[1], [3], [6] - ✓Confirme que o Tracking Server não está exposto a redes não confiáveis e verifique se o plugin opcional de autenticação e as verificações de autorização estão ativos. Restringir a rede complementa, mas não substitui, a atualização.[1]
- ✓Limite a saída de rede do servidor aos destinos necessários e bloqueie loopback, link-local, redes privadas e metadados de nuvem. Revalide cada redirecionamento e peer conectado no código de requisições externas. Mantenha
MLFLOW_WEBHOOK_ALLOW_PRIVATE_IPSdesativada em produção; permita-a apenas em ambientes isolados de desenvolvimento com necessidade explícita.[2], [7] - ✓Adicione testes de regressão na CI para confirmar que endereços não públicos diretos, destinos de redirecionamento, DNS rebinding, pools HTTP/HTTPS e rotas por proxy são bloqueados antes do envio dos dados; teste a exceção para IPs privados separadamente.[2], [7]