GHSA-hjwh-xvfw-qrwj
GHSA-hjwh-xvfw-qrwj · SearXNG Basic Authentication Credentials Exposed Through MCP Logs and JSON-RPC Error Responses
Diese Seite enthält Fakten aus öffentlichen Sicherheitsquellen. Lesen Sie vor dem Aktualisieren die Originalmeldung.
Diese Seite zeigt ausgewählte Angaben aus den Quellen. Die vollständige Beschreibung und betroffene Versionen finden Sie in den verlinkten Sicherheitshinweisen.
### Summary mcp-searxng version 1.11.0 exposes SearXNG Basic Authentication credentials embedded in the `SEARXNG_URL` environment variable. When the server starts in STDIO mode and an MCP client connects, the complete `SEARXNG_URL`, including its username and password, is sent to the client through an MCP `notifications/message` logging notification. Additionally, when URL validation fails, the complete credential-bearing URL is included in the configuration error. This error is logged through MCP and returned to the client as a JSON-RPC error response. For example, a value such as: ```text http://username:[email protected] ``` is exposed without redaction. A connected MCP client or anyone with access to captured server logs may recover the SearXNG credentials and use them to access the configured SearXNG instance. The issue was confirmed in: ```text mcp-searxng 1.11.0 ``` Suggested severity: **Medium** ### Details mcp-searxng supports SearXNG Basic Authentication by embedding credentials in the URL userinfo component: ```text https://username:[email protected] ``` The project contains a redaction function named `redactSearxngInstanceUrl()`, but it is not used in several logging and error-handling paths. #### Startup console disclosure In `src/index.ts:373-378`, the server retrieves the raw SearXNG URLs and writes them directly to stderr: ```typescript const searxngInstances = getSearxngInstances(); if (searxngInstances.length > 0) { console.error(`🌐 SearXNG URLs: ${searxngInstances.join("; ")}`); } ``` `getSearxngInstances()` returns the unmodified environment-variable values. Relevant code in `src/searxng-instances.ts:25-38`: ```typescript export function parseSearxngUrls( raw: string | undefined = process.env.SEARXNG_URL ): string[] { if (raw === undefined) { return []; } return raw .split(";") .map((entry) => entry.trim()) .filter((entry) => entry !== ""); } export function getSearxngInstances(): string[] { return parseSearxngUrls(); } ``` #### MCP logging notification disclosure After the MCP client connects, `src/index.ts:388-393` sends the complete URL through the MCP logging interface: ```typescript const searxngInstances = getSearxngInstances(); logMessage( mcpServer, "info", `SearXNG URLs: ${ searxngInstances.length > 0 ? searxngInstances.join("; ") : "not configured" }` ); ``` `logMessage()` passes this value to `sendLoggingMessage()` in `src/logging.ts:15-25`: ```typescript mcpServer.sendLoggingMessage({ level, data: notificationData }); ``` As a result, the connected MCP client receives a message containing the username and password: ```json { "method": "notifications/message", "params": { "level": "info", "data": { "message": "SearXNG URLs: http://username:[email protected]" } }, "jsonrpc": "2.0" } ``` #### Configuration error disclosure The URL validation function includes the complete unredacted value in error messages. Relevant code in `src/searxng-instances.ts:44-52`: ```typescript export function validateSearxngInstanceUrl( value: string ): string | null { try { const url = new URL(value); if (!["http:", "https:"].includes(url.protocol)) { return `SEARXNG_URL invalid protocol for "${value}": ${url.protocol}`; } } catch { return `SEARXNG_URL invalid format: ${value}`; } return null; } ``` The validation error is aggregated by `validateEnvironment()` in `src/error-handler.ts:175-203`: ```typescript const validationError = validateSearxngInstanceUrl(searxngUrl); if (validationError) { issues.push(validationError); } ``` The complete error is then thrown from `src/search.ts:689-693`: ```typescript const validationError = validateEnvironment(); if (validationError) { logMessage(mcpServer, "error", "Configuration invalid"); throw new MCPSearXNGError(validationError); } ``` The tool handler in `src/index.ts:254-260` sends the error message and stack trace through MCP logging, then rethrows it: ```typescript logMessage( mcpServer, "error", `Tool execution error: ${ error instanceof Error ? error.message : String(error) }`, { tool: name, args: args, error: error instanceof Error ? error.stack : String(error) } ); throw error; ``` Rethrowing the error causes the same unredacted credential-bearing URL to be returned in the JSON-RPC error response. #### Existing redaction function is not used The project already contains a suitable redaction function in `src/searxng-instances.ts:57-69`: ```typescript export function redactSearxngInstanceUrl( raw: string ): string { try { const url = new URL(raw); if (!url.username && !url.password) { return raw; } url.username = ""; url.password = ""; return url.toString(); } catch { return raw.replace( /^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/]*@/, "$1" ); } } ``` However, this function is not applied before startup logging, MCP logging, or configuration error construction. The MCP manifest also marks `SEARXNG_URL` as non-secret in `.mcp/server.json:20-25`: ```json { "name": "SEARXNG_URL", "description": "URL of your SearXNG instance", "isRequired": true, "isSecret": false, "format": "string" } ``` Because credentials may be embedded in this variable, it should be classified as a secret. ### PoC The following proof of concept uses fake credentials. A real SearXNG server is not required. #### Requirements ```text Node.js 20 or newer npm mcp-searxng 1.11.0 source code ``` #### Build the application ```bash unzip mcp-searxng-main.zip cd mcp-searxng-main npm ci npm run build ``` #### Test 1: Credential disclosure through MCP logging Create an MCP initialization request: ```bash cat > /tmp/mcp-init.jsonl <<'EOF' {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"credential-leak-poc","version":"1.0.0"}}} EOF ``` Start the server with fake credentials embedded in a valid HTTP URL: ```bash SEARXNG_URL='http://MCP_POC_USER_7391:[email protected]:9' \ timeout 8s node dist/cli.js \ < /tmp/mcp-init.jsonl \ 2>&1 | tee credential-log-leak.txt ``` Search the output for the credentials: ```bash grep -nE \ 'MCP_POC_USER_7391|MCP_POC_PASS_7391' \ credential-log-leak.txt ``` #### Observed result The complete credential-bearing URL is exposed: ```text SearXNG URLs: http://MCP_POC_USER_7391:[email protected]:9 ``` It is also delivered to the MCP client: ```json { "method": "notifications/message", "params": { "level": "info", "data": { "message": "SearXNG URLs: http://MCP_POC_USER_7391:[email protected]:9" } }, "jsonrpc": "2.0" } ``` This confirms that a connected MCP client can recover the configured username and password without accessing the host environment. #### Test 2: Credential disclosure through JSON-RPC errors Create initialization and tool-call requests: ```bash cat > /tmp/mcp-error-poc.jsonl <<'EOF' {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"credential-error-poc","version":"1.0.0"}}} {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"searxng_web_search","arguments":{"query":"credential leak test"}}} EOF ``` Start the server with a credential-bearing URL that uses an unsupported protocol: ```bash SEARXNG_URL='ftp://MCP_POC_USER_7391:[email protected]' \ timeout 8s node dist/cli.js \ < /tmp/mcp-error-poc.jsonl \ 2>&1 | tee credential-error-leak.txt ``` Search the response: ```bash grep -nE \ 'MCP_POC_USER_7391|MCP_POC_PASS_7391' \ credential-error-leak.txt ``` #### Observed result The complete URL is exposed in the MCP logging notification: ```text Tool execution error: Configuration Issues: SEARXNG_URL invalid protocol
CVSS 5.5 · Mittel · Korrektur verfügbar
- Veröffentlicht
- Aktualisiert
Technologie-Stack: node
Betroffene Produkte und Versionen
mcp-searxng npm
- Betroffene Versionsbereiche
- SEMVER: < 1.12.0
Auf 1.12.0 aktualisieren