Summary
When moling is started in SSE (Server-Sent Events) mode using the -l flag, the HTTP server does not implement any authentication mechanism. Additionally, the underlying mcp-go library hardcodes Access-Control-Allow-Origin: * in the HTTP response headers for the /sse endpoint.
This combination allows any web page—served from any origin—to establish a cross-origin EventSource connection to the /sse endpoint, fully read the SSE stream, and extract the sessionId, which can subsequently be used to perform unauthorized actions against the server.
Vulnerability Details
Under normal browser security rules, the Same-Origin Policy (SOP) prevents scripts on evil-attacker.com from reading responses from 127.0.0.1. However, the presence of Access-Control-Allow-Origin: * explicitly instructs the browser to lift this restriction, allowing full cross-origin reads of the SSE stream.
Attack scenario:
- Victim runs
moling -l 127.0.0.1:6789
- Victim visits a malicious web page (from any origin)
- The malicious page opens an
EventSource to http://127.0.0.1:6789/sse
- The browser permits this due to
Access-Control-Allow-Origin: *
- The malicious page reads the
endpoint event and extracts the sessionId
- The attacker uses the leaked
sessionId to send arbitrary commands to the MCP server
Proof of Concept (simplified):
const es = new EventSource('http://127.0.0.1:6789/sse');
es.addEventListener('endpoint', (e) => {
const sessionId = e.data.split('sessionId=')[1];
console.log('Leaked sessionId:', sessionId);
// Attacker can now POST to /message?sessionId=<leaked> to control the server
});
Root Cause
- No authentication: The
/sse and /message HTTP endpoints accept all requests without any credential verification.
- Hardcoded CORS wildcard: The
mcp-go dependency unconditionally sets Access-Control-Allow-Origin: *, bypassing the browser's Same-Origin Policy.
Impact
An attacker who can trick a victim running moling in SSE mode into visiting a malicious web page can:
- Steal the
sessionId credential from the SSE stream
- Send arbitrary MCP tool calls to the local
moling server
- Potentially achieve local file read/write, command execution, or other actions depending on configured MCP tools
🔧 Patches / Fix
Fixed in PR #48 — commit ae07f704.
Changes implemented:
- CORS stripping — Introduced
corsRemoverResponseWriter that wraps http.ResponseWriter to intercept and remove the Access-Control-Allow-Origin: * header injected by mcp-go before responses reach the client. SSE streaming (http.Flusher) is preserved.
- Token authentication — Added
sseSecurityMiddleware that requires a bearer token on every request, accepted via Authorization: Bearer <token> header or ?token= query parameter. Uses crypto/subtle.ConstantTimeCompare to prevent timing attacks.
- Token provisioning —
NewMoLingServer auto-generates a 32-hex-character token (crypto/rand, 16 bytes) when ListenAddr is set and no token is configured. The token and a ready-to-use URL are printed to stdout only—never persisted to log files.
- Middleware chain — Enforces
sseSecurityMiddleware → requireJSONContentType → SSEServer. The requireJSONContentType layer rejects POST requests without application/json Content-Type, ensuring cross-origin POSTs always trigger a CORS preflight.
- Config & CLI — Added
MoLingConfig.AuthToken field, configurable via --token / -t CLI flag.
🛡️ Workarounds
Users who cannot upgrade immediately should:
- Avoid using SSE mode (
-l flag) in environments where the machine may be exposed to untrusted web content.
- Use a firewall or network policy to restrict access to the listening port (e.g., bind only to
127.0.0.1 and block external access).
- Avoid browsing untrusted websites while
moling is running in SSE mode.
👥 Credits / Acknowledgements
This vulnerability was discovered and responsibly reported by:
- Songwu (security researcher)
- Zeyu Luo (security researcher)
- Dr. CAO Yinfeng
- Kevin
Summary
When
molingis started in SSE (Server-Sent Events) mode using the-lflag, the HTTP server does not implement any authentication mechanism. Additionally, the underlyingmcp-golibrary hardcodesAccess-Control-Allow-Origin: *in the HTTP response headers for the/sseendpoint.This combination allows any web page—served from any origin—to establish a cross-origin
EventSourceconnection to the/sseendpoint, fully read the SSE stream, and extract thesessionId, which can subsequently be used to perform unauthorized actions against the server.Vulnerability Details
Under normal browser security rules, the Same-Origin Policy (SOP) prevents scripts on
evil-attacker.comfrom reading responses from127.0.0.1. However, the presence ofAccess-Control-Allow-Origin: *explicitly instructs the browser to lift this restriction, allowing full cross-origin reads of the SSE stream.Attack scenario:
moling -l 127.0.0.1:6789EventSourcetohttp://127.0.0.1:6789/sseAccess-Control-Allow-Origin: *endpointevent and extracts thesessionIdsessionIdto send arbitrary commands to the MCP serverProof of Concept (simplified):
Root Cause
/sseand/messageHTTP endpoints accept all requests without any credential verification.mcp-godependency unconditionally setsAccess-Control-Allow-Origin: *, bypassing the browser's Same-Origin Policy.Impact
An attacker who can trick a victim running
molingin SSE mode into visiting a malicious web page can:sessionIdcredential from the SSE streammolingserver🔧 Patches / Fix
Fixed in PR #48 — commit
ae07f704.Changes implemented:
corsRemoverResponseWriterthat wrapshttp.ResponseWriterto intercept and remove theAccess-Control-Allow-Origin: *header injected bymcp-gobefore responses reach the client. SSE streaming (http.Flusher) is preserved.sseSecurityMiddlewarethat requires a bearer token on every request, accepted viaAuthorization: Bearer <token>header or?token=query parameter. Usescrypto/subtle.ConstantTimeCompareto prevent timing attacks.NewMoLingServerauto-generates a 32-hex-character token (crypto/rand, 16 bytes) whenListenAddris set and no token is configured. The token and a ready-to-use URL are printed to stdout only—never persisted to log files.sseSecurityMiddleware → requireJSONContentType → SSEServer. TherequireJSONContentTypelayer rejects POST requests withoutapplication/jsonContent-Type, ensuring cross-origin POSTs always trigger a CORS preflight.MoLingConfig.AuthTokenfield, configurable via--token/-tCLI flag.🛡️ Workarounds
Users who cannot upgrade immediately should:
-lflag) in environments where the machine may be exposed to untrusted web content.127.0.0.1and block external access).molingis running in SSE mode.👥 Credits / Acknowledgements
This vulnerability was discovered and responsibly reported by: