Skip to content

[Security] SSRF in web crawl tool + Missing authentication on User API endpoints #4

@angelacook-commits

Description

@angelacook-commits

Security Vulnerability Report

Reporter: @bitox76
Severity: High / Medium
Affected Version: Latest (main branch as of Feb 2026)


Overview

During a security audit of MCP-Workspace-Server, I identified 3 verified security vulnerabilities. The most impactful are: (1) Server-Side Request Forgery (SSRF) in the web crawl tool allowing access to internal services and cloud metadata, and (2) missing authentication on User API endpoints enabling unauthorized file access.


Vulnerability 1: Server-Side Request Forgery (SSRF) in Web Crawl Tool

CWE-918 | CVSS 3.1: 6.5 Medium-High

File: mcp_filesystem/tools/web_crawl_tools.py_validate_url() function (lines ~63-68)

Description: The URL validation only checks for http/https scheme and non-empty netloc, but does NOT block:

  • Private/internal IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
  • Loopback addresses (127.0.0.0/8)
  • Link-local addresses (169.254.0.0/16)
  • Cloud metadata endpoints (169.254.169.254)
  • Known internal hostnames (localhost, metadata.google.internal, etc.)

Vulnerable Code:

def _validate_url(url: str) -> Optional[str]:
    parsed = urlparse(url)
    if parsed.scheme not in ("http", "https"):
        return "仅支持 http/https URL"
    if not parsed.netloc:
        return "URL 缺少域名"
    return None  # <-- No private IP or internal host blocking

After validation, the URL is passed directly to crawler.arun(url=url).

Proof of Concept:

{
  "tool": "crawl_url",
  "arguments": {
    "url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"
  }
}

On AWS/GCP/Azure, this retrieves cloud instance credentials and metadata.

{
  "tool": "crawl_url",
  "arguments": { "url": "http://192.168.1.1/admin" }
}

This accesses internal network services that should not be reachable from the server.

Suggested Fix:

import ipaddress
import socket

BLOCKED_NETWORKS = [
    ipaddress.ip_network("10.0.0.0/8"),
    ipaddress.ip_network("172.16.0.0/12"),
    ipaddress.ip_network("192.168.0.0/16"),
    ipaddress.ip_network("127.0.0.0/8"),
    ipaddress.ip_network("169.254.0.0/16"),
    ipaddress.ip_network("::1/128"),
]

def _validate_url(url: str) -> Optional[str]:
    parsed = urlparse(url)
    if parsed.scheme not in ("http", "https"):
        return "仅支持 http/https URL"
    if not parsed.netloc:
        return "URL 缺少域名"
    
    # Resolve hostname and check against blocked networks
    hostname = parsed.hostname
    if hostname in ("localhost", "metadata.google.internal"):
        return "Blocked internal hostname"
    try:
        ip = ipaddress.ip_address(socket.gethostbyname(hostname))
        for network in BLOCKED_NETWORKS:
            if ip in network:
                return f"Blocked private/internal IP: {ip}"
    except (socket.gaierror, ValueError):
        pass
    return None

Vulnerability 2: Missing Authentication on User API Endpoints

CWE-306 | CVSS 3.1: 7.5 High

File: mcp_filesystem/http_routes.py — multiple User API handlers (~lines 390-760)

Description: User API endpoints (user_workspace_tree, api_workspace_file_download, api_workspace_file_preview, etc.) rely solely on client-supplied user_id and chat_id from query parameters or headers, with NO server-side authentication or authorization.

Vulnerable Pattern:

user_id = request.query_params.get("user_id") or request.headers.get("x-user-id")
chat_id = request.query_params.get("chat_id") or request.headers.get("x-chat-id")
# No authentication check!
workspace_name = get_workspace_name(user_id, chat_id)  # format: {user_id}_{chat_id}

The workspace naming scheme {user_id}_{chat_id} is trivially guessable/enumerable.

PoC:

# Access victim's workspace files
curl "http://target:18089/api/user/workspace/tree?user_id=victim&chat_id=abc123"
curl "http://target:18089/api/workspace/file/download?user_id=victim&chat_id=abc123&file_path=secret.txt"

Vulnerability 3: Unreachable Code Bug in Directory Tree (Logic Error)

CWE-561 | Severity: Low

File: mcp_filesystem/advanced.py_build_tree_node() (lines 193-221)

The try block is incorrectly indented under a continue statement, making it unreachable when a pattern filter is used. This causes directory_tree with a pattern argument to return empty/incorrect results.


Recommendations

  1. P0: Add SSRF protection by blocking private IPs and cloud metadata endpoints in the URL validator.
  2. P0: Add authentication and authorization for all User API endpoints.
  3. P1: Fix indentation bug in advanced.py.

Responsible Disclosure: I am reporting this in good faith to improve the project's security. If you have a preferred private security reporting channel, please let me know. I am happy to coordinate on fixes and provide additional details.

I would appreciate if the maintainers could request CVE identifiers for the SSRF and Missing Auth vulnerabilities. Credit: @bitox76

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions