flawopen.com/AI Security/wazuh-mcp-server-webshell-check-ssrf-ghsa-pw2j-pj4h-f5vg
Défaut SSRF de l’outil de vérification webshell Wazuh MCP — GHSA-pw2j-pj4h-f5vg
Un nom d’hôte contournait le contrôle des adresses IP littérales de Wazuh-MCP-Server avant qu’un outil forensique ne le consulte. La GHSA attribue une gravité élevée et CWE-918 ; aucun CVE n’est attribué. Correctif dans le commit 2bbfe12.[1], [2], [3]
Une réceptionniste vérifie qu’un badge ne figure pas sur une liste de blocage, puis laisse son porteur choisir un bureau. Vérifier seulement le badge imprimé ne montre pas que le nom mène à une salle interdite. Le correctif vérifie où le nom est résolu et oblige la connexion à utiliser cette destination contrôlée.[1], [3]
Concepts Clés et Termes
Server-side request forgery (SSRF)- Le serveur envoie une requête réseau vers une destination influencée par son appelant. Un contrôle incomplet peut lui permettre d’atteindre des réseaux inaccessibles directement à cet appelant.[4], [1]
Résolution DNS et rebinding- Un nom d’hôte est converti en une ou plusieurs adresses IP. Si seul le texte est contrôlé ou si le client HTTP résout à nouveau le nom, la connexion peut viser une adresse différente de celle vérifiée.[1], [3]
Épinglage de destination- L’adresse IP vérifiée est liée à la connexion sortante, tandis que le nom d’hôte est conservé pour HTTP et TLS. Cela ferme l’écart entre validation et connexion.[3]
Correspondances de référentiels
- Référence CWE pour la faiblesse décrite dans ce guide : CWE-918
- D3-OTF Filtrage du trafic sortant — La restriction du trafic sortant décrite dans ce guide limite les destinations accessibles au service ou à l’hôte.
Déroulement de l'Attaque Étape par Étape
Une URL issue de données forensiques atteint l’outil
L’outil concerné blueteam_check_webshell reçoit une URL afin qu’un analyste examine une adresse soupçonnée d’héberger un webshell. L’avis limite le défaut à cet outil, pas à toutes les fonctions de Wazuh-MCP-Server.[1]
Le client HTTP résout l’hôte et suit les redirections
L’URL validée était ensuite transmise à curl, qui effectuait sa propre résolution DNS. L’avis documente aussi le suivi automatique des redirections sans nouvelle validation. Il y a risque SSRF si le serveur peut atteindre une adresse interne et si l’outil reçoit une URL influencée par une source non fiable. L’impact dépend du réseau et des droits d’accès à l’outil.[1], [3]
Le correctif valide et épingle les adresses résolues
Le commit 2bbfe12 résout les enregistrements IPv4 et IPv6, rejette les adresses non publiques sauf pour les domaines explicitement configurés dans ALLOWED_INTERNAL_DOMAINS, puis épingle l’adresse de la requête. Le chemin corrigé ne suit pas automatiquement les redirections. La GHSA attribue une gravité élevée et CWE-918 ; aucun CVE ni intervalle de versions n’est indiqué.[1], [3]
Code Source : Vulnérable vs Sécurisé
L’extrait vulnérable reproduit la logique pertinente de WebshellCheckInput.validate_url du commit 6bd1fa8 ; les imports et les champs de classe sont omis. Il rejette les IP littérales non publiques, mais traite l’erreur ValueError d’un nom d’hôte comme un domaine et l’accepte sans résolution DNS. L’extrait corrigé reproduit _pinned_curl_args du commit 2bbfe12. Le validateur corrigé appelle aussi _host_pins ; la boucle de requêtes transmet ces arguments --resolve à curl et valide chaque destination de redirection avant le saut suivant. Le correctif n’autorise des destinations internes que pour les domaines configurés explicitement. Ce sont des extraits ciblés, pas un remplacement complet du flux de récupération d’URL.[2], [3]
Extrait source: mcp_server/tools/webshell_check.py — validation 6bd1fa8 ; correctif 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}")
# Rejette les adresses IP privées/réservées dans l’hôte de l’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
Liste de Contrôle de Sécurité pour l'Ingénierie
- ✓Repérez les déploiements avec l’implémentation affectée de
blueteam_check_webshell; l’avis indique des commits, pas une plage de versions du paquet.[1], [2] - ✓Mettez à jour vers le commit
2bbfe12ou une version ultérieure comprenant le correctif, puis vérifiez le code déployé.[3] - ✓Résolvez tous les enregistrements A et AAAA et refusez toute adresse non publique, sauf exception interne étroitement définie.[3]
- ✓Liez l’adresse vérifiée à la connexion réelle afin qu’une deuxième résolution DNS ne change pas la destination.[3]
- ✓Désactivez les redirections automatiques ou validez et épinglez chaque destination de redirection.[1], [3]
- ✓Limitez l’accès à l’outil et les sorties réseau du serveur ; vérifiez si un utilisateur ou contenu non fiable peut fournir l’URL.[1]
- ✓Testez les réponses DNS mixtes, IPv4, IPv6, exceptions internes, redirections et épinglage sans contacter de vrais services internes.[3]
Sources
- Avis Wazuh-MCP-Server GHSA-pw2j-pj4h-f5vg (sans CVE attribué)
- Code vulnérable du commit 6bd1fa8 — WebshellCheckInput.validate_url
- Commit correctif Wazuh-MCP-Server 2bbfe12
- MITRE CWE-918 : Server-Side Request Forgery
- The Next Web : failles SSRF distinctes dans des implémentations MCP
- Ars Technica: Vulnerability in agents from Google and others exposes structural flaw in MCP