Skip to content

MCP gateways, session stores, clients, and browser bridges: the trust lives outside the tool call

Source: GitHub Security Advisory waves published 2026-09-24 through 2026-09-30 (cross-checked against vendor text). Related existing pages: Sept 15 MCP DNS-rebinding/alternate-auth page, Sept 22 mcp-atlassian server-side-credential deputy page, Aug 6 agent tool-policy/file-fetch page, Aug 18 editor-extension webview supply-chain page.

This cluster is durable because every entry attacks a layer the tool-call authorization model never covers: session identity, client-side trust of servers, browser-origin gates on local bridges, and the agent's own prompt channel. One rule covers the batch: the MCP protocol has more actors than "server checks tool call" — audit each actor's trust separately.

What changed

Advisory Component Boundary Operator value
GHSA-5xqq-w6xh-vwc6 / CVE-2026-79537 (9.1) MetaMCP ≤ 2.4.22 MCP transport session dispatch keyed only on the client-supplied mcp-session-id header — no owner, namespace, or endpoint binding. Per-endpoint authz validates the endpoint's owner, never the session. Active victim session IDs + namespace UUIDs are disclosed unauthenticated by GET /metamcp/health/sessions Session-ID-as-only-credential + a health/debug endpoint that leaks live session IDs = deterministic cross-tenant tool execution with the victim's forwarded credentials. Sweep every multi-user MCP gateway for: (1) session store key shape, (2) unauthenticated session/list/health routes, (3) whether authz binds endpoint, session, or user
GHSA-4qvr-m43m-5qgr / CVE-2026-79538 (9.8) MetaMCP ≤ 2.4.22 Internal MCP inspector proxy endpoint GET /mcp-proxy/server/stdio spawns a stdio transport — code execution on the gateway "Inspector/debug/proxy" endpoints shipped in production builds are first-class RCE surfaces; enumerate admin-adjacent paths (/health, /sessions, /proxy, /inspector, /debug) on every MCP gateway before testing tools at all
GHSA-8rrr-xx35-4q6h / CVE-2026-51994 (9.1) mcp-remote 0.1.32–0.1.38 Client fetches the resource_metadata URL taken from a remote MCP server's WWW-Authenticate header — a malicious MCP server steers the connecting client's HTTP stack to arbitrary URLs (SSRF from the client's network) The MCP trust inversion: everyone audits servers, but the server→client channel (OAuth metadata, WWW-Authenticate, tool-returned URLs/paths) is unauthenticated input into the client's fetch stack. Connect to a hostile server from a scoped VM and watch outbound; treat OAuth-discovery fields as attacker-chosen fetch targets
GHSA-mvq8-g2rm-4rhm / CVE-2026-51996 (9.8) mcp-remote 0.1.16–0.1.38 Remote code execution through getServerUrlHash in src/lib/utils.ts (server-URL-derived value reaching an unsafe sink on the client) Server-chosen URL strings crossing into client-side path/hash/exec sinks — same shape as the Sept 22 mcp-atlassian path-tool class, relocated to the client half
GHSA-fm8p-53ww-hf6w / CVE-2026-61742 DBHub 0.21.2 (bytebase) HTTP MCP transport is unauthenticated by design in documented mode; its browser-origin defense is Origin host == Host host reflected into ACAO — DNS rebinding makes both match, so a malicious site deterministically invokes MCP DB tools (default public schema access) from the victim's browser, no prompt injection, no model Origin==Host equality is not a rebinding defense (restate: gate on resolved IP locality, and assume loopback MCP/DB ports are always listening on dev boxes). Browser→local-MCP is a two-hop chain you can drive end-to-end with an owned rebinding domain; audit any --transport http local tool the same way
GHSA-g524-9c6h-8w7v / CVE-2026-101084 (9.6) Obot < 0.21.1 Access Control Rules never enforced on /mcp-connect — any authenticated user connects to restricted MCP servers given the server ID, spending the platform's stored OAuth credentials ACL coverage is per-endpoint; a connect/proxy endpoint missing from the rule engine is the twin of the Sept 22 "list-time-only allowlist" bug. Enumerate every route that establishes a credential-bearing connection, not just the ones that use it
GHSA-m95h-j2gj-xrgq / CVE-2026-101065 (9.8) Obot (all ≤ d7e6970) The README quickstart command binds 0.0.0.0:8080 with auth disabled by default; disabled auth maps every request to a synthetic nobody user holding Owner+Admin, and the quickstart mounts /var/run/docker.sock → unauth admin → register/launch attacker-controlled MCP servers with host Docker control Documentation-driven insecure defaults are supply-chain-grade: fingerprint docker.sock-mounted agent platforms (banner/version + /health) and test unauth admin before any credential work. Vendor "fix" was documentation — re-test, don't re-read
GHSA-g5f9-3xfg-p9mf / CVE-2026-61732 (10.0) Decepticon (pentest agent) ChatML special-token literals in web-crawl output are not neutralized before composition into LLM messages; BYOK/OpenAI-compatible backends (vLLM, SGLang, Ollama, LM Studio) don't filter special tokens by default → a string planted in a target page forges an operator-role turn, bypassing agent guardrails → command execution inside the Kali container The pentest agent is the attack surface: any agent that crawls untrusted targets and composes results into chat-template strings without escaping <|...|>-style structural tokens can be hijacked by the target. For engagements: plant canary token literals in owned crawl targets and observe whether the agent's subsequent tool calls change; for agent audits: prove whether special-token IDs survive into tokenizer input
GHSA-wfc7-pjhg-rg95 / CVE-2026-102878 (8.1) mcp-chrome-bridge ≤ 1.0.31 Native-server HTTP API origin validation broken → any web page can cross-origin invoke browser-automation tools: script execution, page-content reading, screenshots The browser-bridge equivalent of Acer NitroSense's loopback RPC: any web page you visit is inside the bridge's invocation boundary. On endpoint recon, enumerate localhost bridge ports and drive them from a browser page, not just curl — CORS-gate ≠ origin-allowlist
GHSA-c9wr-qm7p-p6vc / CVE-2026-100721 (9.0) vm2 < 3.12.2 NodeVM require.external custom resolver records allowed dirs as new RegExp('^' + escapeRegExp(path)) without a separator/end anchor → guest code requires the allowlisted module, then the absolute path of a sibling (foo2/index.js passes when foo is allowed) → host-context top-level code execution Sibling-prefix containment strikes again (same class as Wiki.js page rules, decap-server, Jupyter root_dir): a prefix allowlist without a trailing separator/end-anchor admits <allowed>2. Test the no-separator sibling name on every path-prefix or regex-prefix allowlist, sandbox included
GHSA-3gp8-hjh6-xf3r / CVE-2026-102242 google/mcp-go allowedLocalRoots path validation resolves links improperly (CWE-59/22) → roots confinement escapes via link handling MCP SDK-level root confinement still ships symlink bugs — run the symlink canary against file servers built on each major SDK, don't assume the framework's roots guard is proven

Durable axes

  1. Sessions are credentials — audit the key, not the login. A gateway that authorizes endpoints but dispatches sessions by a bare client-supplied ID has no session authorization at all. Proofs: forge/replay a session id between two disposable tenants; check /health, /sessions, /stats for unauthenticated ID disclosure first.
  2. The server→client direction of MCP is an unauthenticated input channel. OAuth metadata URLs, WWW-Authenticate fields, and server-chosen URLs/paths all cross into the client's fetch/file/exec stacks. Threat-model hostile servers explicitly — connect from a network-monitoring VM with a canary file tree.
  3. Browser-origin gates on local bridges are the weakest link (equality checks, CORS misconfig, reflected ACAO). DNS rebinding with TTL-0 A→127.0.0.1 defeats Origin==Host; test bridges from an owned rebinding domain in a real browser.
  4. Agents that ingest untrusted text into chat templates must neutralize structural tokens. <|im_start|>-style literals surviving into tokenizer input = role forgery at CVSS 10. This applies to every recon-crawl→LLM pipeline, including offensive tooling you operate.
  5. Debug/inspector/health routes ship to production and carry more privilege than the tool surface. Enumerate them before testing a single tool.
  6. Documented quickstarts are the default deployment. 0.0.0.0 binds, auth-off examples, and docker.sock mounts in READMEs are the fleet posture; fingerprint and verify, and treat docs-only "fixes" as unpatched.
  7. Prefix allowlists without boundary anchors fail one directory over. Regex ^/allowed and startsWith(root) both admit the sibling. One canonical test tuple per guard: <root>-canary / <allowed>2.

Validation boundaries

Use disposable MCP deployments, synthetic tenant users, and marker rows/sessions only. Drive rebinding and bridge tests from owned domains and your own browser profile. Never replay real session IDs or tokens observed on shared infrastructure beyond a single authorized read-decision check; never invoke host Docker controls outside a lab VM; plant prompt-injection canaries only in targets you own or that authorize it. No live OAuth credentials, tenant data, or production gateway probing in wiki evidence.

Tracked, not published

iperf3 pre-auth heap overflow in decrypt_rsa_message() (pwcx, memory-safety, revisit if weaponized); virtualenv seed-wheel integrity + pyvenv.cfg config injection (94p9/9h9j, repo-trust family — fold if a build-tool page gap appears); fastify HTTP/2 trailer DoS (4mh8); russh SSH-client pack (channel-ID callback fires 47hw, KEM zero-point w3jg — client-side SSH audit interest); Astro Netlify CDN allowlist SSRF (423w) + Host-port crash; JLine-adjacent ReDoS singles; libsoup WebSocket pack; NetX/ThreadX/Zephyr embedded pack (~20, lab-firmware only); Anjvision YSSD-RTMP-H5 firmware pack (hardcoded cloud creds, hidden debug interface — revisit on delivery-chain detail); TOTOLINK/Netcore/D-Link CGI overflow singles (established tracked class); Chrome/Firefox memory-safety trains (browser pwn, no operator workflow); Joomla/ordasoft/lomart SQLi/RCE singles; ServiceNow AI-SQLi pair + GitLab pair (detail-light, class-processed); IBM Guardium CLI cmdi trio (authenticated-CLI class); vm2 resolver fix noted; PraisonAI/agent singles class-processed; OpenClaw iOS TLS-pin non-enforcement (fold candidate on OpenClaw page if one reopens); IO::Socket::SSL::SelfCertificate registry malware (already covered by Perl malware precedent page — SHA sweep rule applies).

October 1 12:31Z follow-up: the deny list forgot the composite route (Obot, folded)

CVE-2026-103758 / GHSA-x6jv-4hp7-3vj9 (high, Obot 0.21.1–0.24.1): the checkUI deny list omits /mcp-connect-composite/ — Basic-role users holding a composite MCP ID proxy through mcpGateway.Proxy and invoke tools on MCP servers that Access Control Rules were supposed to hide. This is the same product and same class as the wave's earlier /mcp-connect ACL miss (101084): when a guard is implemented as a route-name deny list, enumerate the route family (/mcp-connect, /mcp-connect-composite/, any sibling suffix) and test each — the fix for the first leg is exactly where you start fuzzing. Generalize: every deny-list gate gets the sibling-spelling sweep (-composite, -v2, -internal, trailing-slash variants) before you trust the first leg's fix. Validate with two disposable roles and a canary MCP tool that records invocations; never invoke real tenant-restricted tools.

October 1 21:3xZ follow-up: DeepTutor shell tool — the agent's own ExecTool is the RCE route (folded)

DeepTutor v1.4.0 (GHSA-23c2-f3h2-xmcw, 9.8): command execution reachable through /tutorbot/agent/tools/shell.py:ExecTool.execute — the agent framework ships a shell tool and the advisory's sink is that tool's execute path, reached through the agent's normal conversation/tool-call channel.

This is the wave's invariant landing on a consumer-facing tutor product instead of a gateway: once a product embeds an agent with a shell-capable tool, the prompt channel is the command channel — no separate exploit primitive is needed once the model can be steered (direct input, or any content the agent ingests). Triage fingerprint for any agent-product deployment: enumerate its tool registry (tools/, agent/tools/, MCP server listings) and record which tools exist — shell/exec/python_exec/fs_write presence is the risk ceiling, independent of the product's marketing surface. Validation stays on owned deployments with an inert marker command; prove that tool invocation reaches the OS (a marker file), never the customer's host, and report the tool registry + auth posture (who may invoke tools, per-session or shared) as the finding.

October 2 follow-up: the DeepTutor wave completes the picture — tool layer, ID space, and MCP output_path

The Oct 1 DeepTutor ExecTool item was one advisory; the Oct 2 batch (CVE-2026-51876/51878/51879/51880/51881) completes the sweep of the same product and sharpens three rules:

  • The tool layer is reachable through the WebSocket channel, not just HTTP (CVE-2026-51881 ExecTool shell via live tutorbot WebSocket; CVE-2026-51880 EditFileTool writes absolute paths outside the bot workspace). When you fingerprint an agent product, enumerate its transport surface too — a WebSocket "live tutorbot" interface that reaches the same *.execute() tool sinks as the HTTP API is a second, usually less-audited entry point (no CSRF, different auth middleware, often no rate limiting).
  • IDOR across every agent object class: session_id reuse to regenerate another user's turn (51878), enumerable bot_id to overwrite another bot's whitelisted control files over HTTP (51879), publicly exposed book_id to overwrite persisted metadata via confirm-proposal (51876). Agent apps carry a menagerie of object IDs (session, bot, book, conversation, workspace); the authz gate is per-object-class and drifts per class. Run the cross-tenant ID replay per class, not per endpoint.
  • MCP server tool parameters are file-write sinks with a prompt-injection driver: GHSA-xpvr-6r3p-gm34 / CVE-2025-71427, Office-PowerPoint-MCP-Server ≤ 2.0.7 — save_presentation/open_presentation/manage_image accept absolute paths and ../ in output_path, so prompt-injecting an agent that holds this server turns "make me a deck" into overwrite-any-writable-file / read-external-file. Audit rule for every MCP server in your bundle or target's bundle: grep tool schemas for path-ish parameters (path, output_path, filename, file_path) and test whether the server anchors them to a root server-side. The model is not a security boundary; the tool implementation must be.

Validation: owned agent deployments, marker files inside disposable roots, two disposable tenant accounts for the ID-replay matrix, inert MCP tool invocations that only write marker paths.

October 15 follow-up: IntentKit and MoviePilot — the advertised toolset is not the executable toolset, and the approval flag is a model-settable parameter

Two advisories from the Oct 15 03:36Z wave close two gaps in the same agent-tool gate pattern: which tools may run, and who may say "yes" to a dangerous one.

IntentKit ≤2.6.3 — executor validates against the full registry, not the per-request advertisement (GHSA-6qqv-mhm9-qrfj / CVE-2026-76066, high). A ToolBindingMiddleware narrows the tool list advertised per request/context (WEB, API, TWITTER, TELEGRAM), but the executor validates incoming tool calls against the full tool registry. Supplying the exact name of a de-advertised owner-only tool — wallet transfer, posting, memory-write, call-agent — executes it with no human-in-the-loop approval gate, reachable by unauthenticated/public callers via indirect prompt injection or by replaying checkpoint/history data on a reused thread.

MoviePilot ≤3.1.5 — prompt-injected content sets its own approval flag (GHSA-f3rc-mp36-mqvp / CVE-2026-77931, high). Untrusted content entering browse_webpage, search_web, and torrent-metadata parsers can carry instructions that steer the agent into calling execute_command with confirm_dangerous set to true — a tool parameter, so the injected content supplies the confirmation the deny-list check (validate_command_safety) expected to be human-controlled. Execution lands in asyncio.create_subprocess_shell under the admin session's is_admin context → host RCE.

Operator rules:

  1. De-advertised ≠ disabled. In any agent framework that filters tools/list per context, enumerate the full registry from source/config (tools/, registry dicts, middleware fixtures) and probe the execution endpoint with the exact de-advertised names. Advertisement narrowing and execution validation are two code paths; test each independently. This is the tool-plane twin of the disabled-setting-at-router rule — a UI-side filter that the executor never rechecks.
  2. Approval flags as tool arguments are theater. Grep every dangerous tool schema for confirm/approve/skip-safety booleans (confirm_dangerous, force, confirmed). If the model composes the arguments, prompt-injected content can set them. A real gate renders approval outside the tool call (server-side interrupt the model cannot emit). Reproduce with an owned lab agent fed a page containing an inert marker-command instruction; prove invocation reaches a marker file, never a real payload.
  3. History/checkpoint replay is a second injection channel. IntentKit's bypass also fired by replaying persisted checkpoint/thread data — retained conversation state re-enters the executor under the original (higher) context. Sweep persisted-state resume endpoints separately from live input channels.
  4. Deny-list on the command string is layer-mixing bait. validate_command_safety inspecting the command text while the confirmation rides as a parameter means both checks trust model-supplied data. When auditing agent stacks, chart who controls each input to a dangerous tool: model, injected content, or server.