GHSA-g9rg-8vq5-mpwm mcp-memory-service's Wildcard CORS with Credentials Enables Cross-Origin Memory Theft
SummaryWhen the HTTP server is enabled (`MCP_HTTP_ENABLED=true`), the application configures FastAPI's CORSMiddleware with `allow_origins=['*']`, `allow_credentials=True`, `allow_methods=["*"]`, and `allow_headers=["*"]`. The wildcard `Access-Control-Allow-Origin: *` header permits any website to read API responses cross-origin. When combined with anonymous access (`MCP_ALLOW_ANONYMOUS_ACCESS=true`) - the simplest way to get the HTTP dashboard working without OAuth - no credentials are needed, so any malicious website can silently read, modify, and delete all stored memories.DetailsVulnerable Code**`config.py:546` - Wildcard CORS origin default**This produces `['*']` by default, allowing any origin.**`app.py:274-280` - CORSMiddleware configuration**How the Attack WorksThe wildcard CORS default means every API response includes `Access-Control-Allow-Origin: *`. This tells browsers to allow **any website** to read the response. When combined with anonymous access (no authentication required), the attack is straightforward:The browser sends the request, the server responds with `ACAO: *`, and the browser allows the JavaScript to read the response body. No cookies, no auth headers, no credentials of any kind.**Clarification on `allow_credentials=True`:** The advisory originally stated that Starlette reflects the `Origin` header when `allow_credentials=True` with wildcard origins. Testing with Starlette 0.52.1 shows that **actual responses return `ACAO: *`** (not the reflected origin); only preflight `OPTIONS` responses reflect the origin. Per the Fetch specification, browsers block `ACAO: *` when `credentials: 'include'` is used. However, this is irrelevant to the attack because **anonymous access means no credentials are needed** - a plain `fetch()` without `credentials: 'include'` works, and `ACAO: *` allows it.Two Attack VectorsThis misconfiguration enables two distinct attack paths:**1. Cross-origin browser attack (CORS - this advisory)**Attacker lures victim to a malicious webpageJavaScript on the page reads/writes the memory service APIWorks from anywhere on the internet if the victim visits the pageThe `ACAO: *` header is what allows the browser to expose the response to the attacker's JavaScript**2. Direct network access (compounding factor)**Attacker on the same network directly calls the API (`curl http://<target>:8000/api/memories`)No CORS involved - CORS is a browser-only restrictionEnabled by `0.0.0.0` binding + anonymous access, independent of CORS configurationThe CORS misconfiguration specifically enables attack vector #1, extending the reach from local network to anyone who can get the victim to click a link.Compounding Factors**`HTTP_HOST = '0.0.0.0'`** - Binds to all interfaces, exposing the service to the entire network (enables attack vector #2)**`HTTPS_ENABLED = 'false'`** - No TLS by default, allowing passive interception**`MCP_ALLOW_ANONYMOUS_ACCESS`** - When enabled, no authentication is required at all. This is the key enabler: without it, the CORS wildcard alone would not allow data access (the attacker would need to forward valid credentials, which `ACAO: *` blocks)**`allow_credentials=True`** - Bad practice: if a future Starlette version changes to reflect origins (as some CORS implementations do), this would escalate the vulnerability by allowing credential-forwarding attacks against OAuth/API-key users**API key via query parameter** - `api_key` query param is cached in browser history and server logsAttack ScenarioVictim runs `mcp-memory-service` with HTTP enabled and anonymous accessVictim visits `https://evil.com` which includes JavaScriptJavaScript sends `fetch('http://<victim-ip>:8000/api/memories')` (no credentials needed)Server responds with `Access-Control-Allow-Origin: *`Browser allows JavaScript to read the response - attacker receives all memoriesAttacker's script also calls DELETE/PUT endpoints to modify or destroy memoriesVictim sees a normal web page; no indication of the attackRoot CauseThe default value of `MCP_CORS_ORIGINS` is `*`, which allows any website to read API responses. This is a permissive default that should be restricted to the expected dashboard origin (typically `localhost`). The `allow_credentials=True` is an additional misconfiguration that doesn't currently enable the attack.PoCImpact**Complete cross-origin memory access**: Any website can read all stored memories when the victim has the HTTP server running with anonymous access**Memory tampering**: Write/delete endpoints are also accessible cross-origin, allowing memory destruction**Remote attack surface**: Unlike direct network access (which requires LAN proximity), the CORS vector works from anywhere on the internet - the victim just needs to visit a link**Silent exfiltration**: The attack is invisible to the victim; no browser warnings, no popups, no indicatorsRemediationReplace the wildcard default with an explicit localhost origin:Affected DeploymentsThe vulnerability exists in the Python source code and is not mitigated by any deployment-specific configuration. Docker HTTP mode is the highest-risk deployment because it explicitly binds to `0.0.0.0`, maps the port, and does not override the wildcard CORS default.
CVSS v3.1 score: 8.1