FlawOpen

flawopen.com/AI Security/wazuh-mcp-server-webshell-check-ssrf-ghsa-pw2j-pj4h-f5vg

● CWE-918 · Hoch
Sicherheitsforschung · FlawOpen

SSRF-Schwachstelle im Wazuh-MCP-Webshell-Prüfer — GHSA-pw2j-pj4h-f5vg

Ein Hostname umging die Prüfung literaler IP-Adressen in Wazuh-MCP-Server, bevor ein Forensik-Tool ihn abrief. Die GHSA stuft den Fehler als Hoch ein und nennt CWE-918; eine CVE gibt es nicht. Behoben in Commit 2bbfe12.[1], [2], [3]

💡 Einfache Erklärung (ELI5)

Eine Empfangskraft prüft, ob ein Ausweis auf einer Sperrliste steht, und lässt die Person danach ein Büro wählen. Die Prüfung der aufgedruckten Kennung zeigt nicht, dass der Name zu einem gesperrten Raum führt. Die Korrektur prüft die Namensauflösung und bindet die Verbindung an das geprüfte Ziel.[1], [3]

Kernkonzepte & Begriffe

Server-side request forgery (SSRF)
Ein Server sendet eine Netzwerkanfrage an ein Ziel, das der Aufrufer beeinflusst. Unvollständige Prüfungen können den Zugriff auf Netze ermöglichen, die der Aufrufer selbst nicht erreichen kann.[4], [1]
DNS-Auflösung und Rebinding
Ein Hostname wird in eine oder mehrere IP-Adressen aufgelöst. Prüft man nur den Text oder löst der HTTP-Client den Namen später erneut auf, kann die Verbindung ein anderes Ziel verwenden als das geprüfte.[1], [3]
Zieladressbindung
Die geprüfte IP-Adresse wird an die ausgehende Verbindung gebunden; der Hostname bleibt für HTTP- und TLS-Prüfungen erhalten. So wird die Lücke zwischen Prüfung und Verbindung geschlossen.[3]

Framework-Zuordnungen

  • CWE-Referenz für die in diesem Leitfaden beschriebene Schwachstelle: CWE-918
  • D3-OTF Filterung des ausgehenden Datenverkehrs — Die in diesem Leitfaden beschriebene Beschränkung des ausgehenden Netzwerkverkehrs begrenzt die für den Dienst oder Host erreichbaren Ziele.

Schritt-für-Schritt Angriffsablauf

Schritt 1

Eine URL aus Forensikdaten erreicht das Prüfwerkzeug

Das betroffene Tool blueteam_check_webshell nimmt eine URL entgegen, damit Analysten eine Adresse untersuchen können, die möglicherweise ein Webshell hostet. Die Meldung grenzt den Fehler auf dieses Tool ein, nicht auf alle Funktionen von Wazuh-MCP-Server.[1]

Schritt 2

Der Validator prüft nur IP-Literale

Das anfällige WebshellCheckInput.validate_url liest den Host aus und ruft ipaddress.ip_address auf. Eine literale IP lässt sich prüfen; ein DNS-Name löst jedoch ValueError aus und wird ohne A- oder AAAA-Abfrage akzeptiert.[2], [1]

Schritt 3

Der HTTP-Client löst das Ziel auf und folgt Weiterleitungen

Die validierte URL wurde anschließend an curl übergeben, das selbst DNS auflöste. Die Meldung beschreibt außerdem automatisches Folgen von Weiterleitungen ohne erneute Prüfung. SSRF ist möglich, wenn der Server interne Ziele erreichen kann und das Tool eine von nicht vertrauenswürdigen Daten beeinflusste URL erhält. Die tatsächliche Auswirkung hängt von Netz und Toolzugriff ab.[1], [3]

Schritt 4

Der Patch prüft und bindet aufgelöste Adressen

Commit 2bbfe12 löst IPv4- und IPv6-Einträge auf, lehnt nicht öffentliche Adressen ab, sofern der Host nicht ausdrücklich in ALLOWED_INTERNAL_DOMAINS steht, und bindet die gewählte Adresse an die Anfrage. Der korrigierte Abruf folgt Weiterleitungen nicht automatisch. Die GHSA nennt Schweregrad Hoch und CWE-918, aber keine CVE und keinen Paketversionsbereich.[1], [3]

Quellcode: Verwundbar vs. Sicher

Der verwundbare Ausschnitt gibt die relevante Logik aus WebshellCheckInput.validate_url in Commit 6bd1fa8 wieder; Imports und Klassenfelder sind ausgelassen. Er weist nicht öffentliche IP-Literale zurück, behandelt aber den ValueError eines Hostnamens als Domain und akzeptiert ihn ohne DNS-Auflösung. Der gehärtete Ausschnitt gibt _pinned_curl_args aus Fix-Commit 2bbfe12 wieder. Der korrigierte Validator ruft außerdem _host_pins auf; der Anfrageablauf übergibt curl diese --resolve-Argumente und prüft jedes Redirect-Ziel vor dem nächsten Schritt. Interne Ziele sind nur für ausdrücklich konfigurierte Domains zulässig. Das sind gezielte Quelltextauszüge, kein vollständiger Ersatz des URL-Abrufpfads.[2], [3]

Quelltextauszug: mcp_server/tools/webshell_check.py — Prüfung 6bd1fa8; Korrektur 2bbfe12

VERWUNDBARE IMPLEMENTIERUNG
@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}")
    # Private/reservierte IP-Adressen im URL-Host ablehnen.
    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
GEHÄRTETER SICHERHEITS-PATCH
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

Checkliste für Engineering & Systemsicherheit

Quellen

← AI Security Directory →