FlawOpen

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

● CWE-918 · उच्च
सुरक्षा अनुसंधान · FlawOpen

Wazuh MCP के webshell जाँच टूल की SSRF खामी — GHSA-pw2j-pj4h-f5vg

फॉरेंसिक टूल द्वारा अनुरोध किए जाने से पहले एक होस्टनाम ने Wazuh-MCP-Server की literal-IP जाँच को पार कर लिया। GHSA ने गंभीरता High और CWE-918 दी है; कोई CVE नहीं है। commit 2bbfe12 में सुधार किया गया।[1], [2], [3]

💡 आसान भाषा में (ELI5)

रिसेप्शनिस्ट किसी आगंतुक के बैज को रोक सूची से मिलाता है और फिर उसे कमरा चुनने देता है। केवल छपे हुए बैज की जाँच से पता नहीं चलता कि नाम किसी प्रतिबंधित कमरे की ओर जाता है। सुधार नाम के DNS समाधान की जाँच करता है और कनेक्शन को उसी सत्यापित पते पर बाँधता है।[1], [3]

इस पेज के मुख्य शब्द

Server-side request forgery (SSRF)
सर्वर ऐसे नेटवर्क पते पर अनुरोध भेजता है जिसे कॉल करने वाला प्रभावित कर सकता है। अधूरी जाँच से सर्वर उन नेटवर्कों तक पहुँच सकता है जहाँ कॉल करने वाला सीधे नहीं पहुँच सकता।[4], [1]
DNS समाधान और rebinding
होस्टनाम एक या अधिक IP पतों में बदलता है। केवल नाम का पाठ जाँचने या HTTP क्लाइंट द्वारा बाद में फिर से समाधान करने पर वास्तविक जुड़ाव जाँचे गए पते से अलग हो सकता है।[1], [3]
गंतव्य पिन करना
सत्यापित IP को आउटगोइंग कनेक्शन से बाँधा जाता है, जबकि HTTP और TLS जाँचों के लिए होस्टनाम रखा जाता है। इससे जाँच और कनेक्शन के बीच का अंतर बंद होता है।[3]

फ्रेमवर्क मैपिंग

  • इस गाइड में बताई गई कमजोरी के लिए CWE संदर्भ: CWE-918
  • D3-OTF आउटबाउंड ट्रैफ़िक फ़िल्टरिंग — इस गाइड में दिया गया आउटबाउंड नेटवर्क प्रतिबंध सेवा या होस्ट के पहुँच योग्य गंतव्यों को सीमित करता है।

हमले का चरण-दर-चरण प्रवाह

चरण 1

फॉरेंसिक डेटा का URL जाँच टूल तक पहुँचता है

प्रभावित blueteam_check_webshell टूल ऐसा URL लेता है जिससे विश्लेषक संदिग्ध webshell पते की जाँच कर सके। सलाहकार ने समस्या को इसी टूल तक सीमित किया है, Wazuh-MCP-Server की सभी क्षमताओं तक नहीं।[1]

चरण 2

validator केवल literal IP जाँचता है

कमज़ोर WebshellCheckInput.validate_url होस्ट निकालकर ipaddress.ip_address को देता है। literal IP की जाँच होती है, लेकिन DNS नाम पर ValueError आता है और बिना A या AAAA रिकॉर्ड देखे नाम स्वीकार हो जाता है।[2], [1]

चरण 3

HTTP क्लाइंट खुद समाधान करता और redirect का अनुसरण करता है

इसके बाद मान्य URL curl को दिया जाता था, जो अपना DNS समाधान करता था। सलाहकार बिना दोबारा जाँच के automatic redirect का अनुसरण भी बताता है। यदि सर्वर आंतरिक पते तक पहुँच सकता है और अविश्वसनीय इनपुट URL नियंत्रित करता है, तो SSRF जोखिम बनता है। वास्तविक प्रभाव नेटवर्क पहुँच और टूल अनुमति पर निर्भर है।[1], [3]

चरण 4

पैच समाधान किए गए पते जाँचकर पिन करता है

commit 2bbfe12 IPv4 और IPv6 रिकॉर्ड का समाधान करता है, ALLOWED_INTERNAL_DOMAINS में स्पष्ट रूप से दिए गए होस्ट को छोड़कर गैर-सार्वजनिक पते अस्वीकार करता है, और चुने हुए पते को अनुरोध से बाँधता है। सुधरा हुआ fetch path redirect का स्वतः अनुसरण नहीं करता। GHSA ने High और CWE-918 दिया; CVE या पैकेज संस्करण सीमा नहीं दी।[1], [3]

सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन

कमज़ोर अंश commit 6bd1fa8 के WebshellCheckInput.validate_url का संबंधित logic दिखाता है; imports और class fields हटाए गए हैं। यह non-public literal IP को रोकता है, लेकिन hostname पर ValueError को domain मानकर बिना DNS resolution स्वीकार कर लेता है। सुधार वाला अंश fix commit 2bbfe12 के _pinned_curl_args को दिखाता है। सुधरा validator _host_pins भी बुलाता है; request loop --resolve arguments curl को देता है और अगले hop से पहले हर redirect destination की जाँच करता है। Internal destination केवल स्पष्ट रूप से configured domains के लिए अनुमति है। ये केंद्रित source excerpts हैं, पूरा URL-fetching flow नहीं।[2], [3]

सोर्स अंश: mcp_server/tools/webshell_check.py — जाँच 6bd1fa8; सुधार 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}")
    # URL host में private/reserved IP को अस्वीकार करें।
    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

इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट

स्रोत

← AI Security Directory →