flawopen.com/AI Security/wazuh-mcp-server-webshell-check-ssrf-ghsa-pw2j-pj4h-f5vg
Falha SSRF no verificador de webshell do Wazuh MCP — GHSA-pw2j-pj4h-f5vg
Um nome de host contornava a verificação de IP literal do Wazuh-MCP-Server antes de uma ferramenta forense acessá-lo. A GHSA atribui gravidade Alta e CWE-918; não há CVE. Corrigido no commit 2bbfe12.[1], [2], [3]
Uma recepcionista confere se o crachá de uma pessoa não está numa lista de bloqueio e depois deixa que ela escolha o escritório. Conferir apenas o crachá impresso não revela que o nome da pessoa aponta para uma sala restrita. A correção verifica para onde o nome é resolvido e faz a conexão usar esse destino conferido.[1], [3]
Conceitos Centrais e Termos
Server-side request forgery (SSRF)- O servidor faz uma requisição de rede para um destino influenciado por quem o chama. Verificações incompletas podem permitir acesso a redes que a pessoa não alcança diretamente.[4], [1]
Resolução DNS e rebinding- Um nome de host é convertido em um ou mais endereços IP. Se a validação só verifica o texto ou o cliente HTTP resolve novamente depois, o endereço usado pode ser diferente do que foi verificado.[1], [3]
Fixação do destino- O IP validado é associado à conexão de saída, mantendo o nome de host para as verificações HTTP e TLS. Isso fecha a diferença entre validar e conectar.[3]
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.
Fluxo de Ataque Passo a Paso
Uma URL de dados forenses chega ao verificador
A ferramenta afetada blueteam_check_webshell recebe uma URL para que o analista examine um endereço suspeito de hospedar um webshell. O aviso limita a falha a essa ferramenta, não a todas as funções do Wazuh-MCP-Server.[1]
O cliente HTTP resolve e pode seguir redirecionamentos
Depois, a URL validada era enviada ao curl, que fazia sua própria resolução DNS. O aviso também documenta redirecionamentos automáticos sem nova validação. Isso cria exposição a SSRF quando o servidor alcança endereços internos e a ferramenta recebe uma URL controlada por uma entrada não confiável. O impacto depende da rede e do acesso à ferramenta.[1], [3]
A correção valida e fixa os endereços resolvidos
O commit 2bbfe12 resolve registros IPv4 e IPv6, rejeita endereços não públicos salvo quando o domínio está em ALLOWED_INTERNAL_DOMAINS e fixa o endereço usado pela requisição. O caminho corrigido não segue redirecionamentos automaticamente. A GHSA atribui gravidade Alta e CWE-918, sem CVE ou faixa de versões de pacote.[1], [3]
Código-Fonte: Vulnerável vs. Seguro
O trecho vulnerável reproduz a lógica relevante de WebshellCheckInput.validate_url do commit 6bd1fa8; imports e campos da classe foram omitidos. Ele rejeita IPs literais não públicos, mas trata ValueError de um hostname como domínio e o aceita sem resolução DNS. O trecho corrigido reproduz _pinned_curl_args do commit 2bbfe12. O validador corrigido também chama _host_pins; o fluxo de busca passa os argumentos --resolve ao curl e valida cada destino de redirecionamento antes do próximo salto. O patch só permite destinos internos para domínios configurados explicitamente. São trechos focados, não uma substituição completa do fluxo de busca de URLs.[2], [3]
Trecho de origem: mcp_server/tools/webshell_check.py — validação 6bd1fa8; correção 2bbfe12
@field_validator("url")
@classmethod
def validate_url(cls, v: str) -> str:
v = v.strip()
if not v.startswith(("http://", "https://")):
raise ValueError(f"URL must start with http:// or https://, got: {v!r}")
# Rejeita IPs privados/reservados no host da URL.
from urllib.parse import urlparse
import ipaddress
host = urlparse(v).hostname
if host:
try:
ip = ipaddress.ip_address(host)
except ValueError:
ip = None # not an IP - domain name, allowed
if ip is not None and (ip.is_private or ip.is_loopback or ip.is_reserved or ip.is_link_local):
raise ValueError(f"URL host is a private/reserved IP: {host}. This tool only accepts public hosts.")
return v
def _pinned_curl_args(url: str) -> tuple[list[str], str | None]:
"""Build curl --resolve args for an already-validated destination."""
import ipaddress
u = urlparse(url)
host = u.hostname
if not host:
return [], "URL has no host."
try:
ipaddress.ip_address(host)
return [], None # Literal IP: no DNS lookup to rebind.
except ValueError:
pass
port = u.port or (443 if u.scheme == "https" else 80)
ips, err = _host_pins(host, _allowed_internal_domains())
if err:
return [], err
args: list[str] = []
for ip in ips:
addr = f"[{ip}]" if ":" in ip else ip
args += ["--resolve", f"{host}:{port}:{addr}"]
return args, None
Lista de Verificação de Segurança para Engenharia
- ✓Localize implantações com a implementação afetada de
blueteam_check_webshell; o aviso informa commits, não uma faixa de versões de pacote.[1], [2] - ✓Atualize para o commit
2bbfe12ou uma versão posterior com a correção e confirme o código implantado.[3] - ✓Resolva todos os registros A e AAAA e rejeite endereços não públicos, salvo exceção interna de domínio estritamente necessária.[3]
- ✓Associe o IP validado à conexão real para impedir que uma segunda consulta DNS mude o destino após a validação.[3]
- ✓Desative redirecionamentos automáticos ou valide e fixe cada destino redirecionado.[1], [3]
- ✓Restrinja o acesso à ferramenta e a saída de rede do servidor; verifique se usuários ou conteúdo não confiáveis controlam o argumento de URL.[1]
- ✓Teste DNS misto público e não público, IPv4, IPv6, exceções de domínios internos, redirecionamentos e fixação sem acessar serviços internos reais.[3]
Fontes
- Aviso Wazuh-MCP-Server GHSA-pw2j-pj4h-f5vg (sem CVE)
- Código vulnerável no commit 6bd1fa8 — WebshellCheckInput.validate_url
- Commit de correção Wazuh-MCP-Server 2bbfe12
- MITRE CWE-918: Server-Side Request Forgery
- The Next Web: falhas SSRF distintas em implementações MCP
- Ars Technica: Vulnerability in agents from Google and others exposes structural flaw in MCP