Security

Agent Router: MCP session hijacking via unchecked subject and default seed

Agent Router (formerly Envoy AI Gateway) MCP proxy - session resume does not verify the embedded anti-hijacking subject; default Helm seed makes session IDs forgeable (GHSA-953h-535j-jg2v)

PUBLISHED SEVERITY HIGHSTATUS PatchedGHSA-953h-535j-jg2v
Patched

Fixed in Agent Router 1.2.0 (2026-10-07): #2740 binds MCP sessions to the verified subject, and #2755 makes the Helm chart generate a random seed instead of the public default. Track the fix at theagentrouter/agent-router#2740.

Product
Agent Router, formerly Envoy AI Gateway (Go module github.com/envoyproxy/ai-gateway, Helm chart ai-gateway-helm) in theagentrouter/agent-router
Affected versions
Agent Router / Envoy AI Gateway 1.1.0 and earlier with MCP enabled. Fixed in 1.2.0 (2026-10-07).
Severity
HIGH
Status
Patched
Weaknesses
  • CWE-798Use of Hard-coded Credentials
  • CWE-863Incorrect Authorization
As filed on the advisory.
GitHub advisory
GHSA-953h-535j-jg2v
Published by the maintainer on 2026-10-07
CVE
Not requested yet
Published
Credit
Finder: Aaron Elijah Mars of Aeon. Tool: Aeon (https://www.aeon.fun). Co-reporters credited on the advisory: PraveenKumarInjam, kanywst.

What users should do now

Upgrade to Agent Router 1.2.0 (released 2026-10-07). It includes #2740 (subject check) and #2755 (random seed in the Helm chart).

  1. Upgrade the controller, the extproc image and the ai-gateway-helm chart to 1.2.0. Upgrading changes the seed, which ends active MCP sessions; clients must initialize again.
  2. If you set controller.mcp.sessionEncryption.fallback.seed=default-insecure-seed to keep sessions alive during the upgrade, remove it as soon as clients have reconnected. While it is set, the gateway still accepts session IDs encrypted with the public seed.
  3. If you render the chart with helm template (GitOps), set controller.mcp.sessionEncryption.existingSecret or a long random seed. Otherwise every render makes a new seed.
  4. If you run the controller or extproc outside the chart, always pass --mcpSessionEncryptionSeed with a secret random value. The binaries still default to default-insecure-seed; 1.2.0 only logs a warning.
  5. Until you can upgrade, set controller.mcp.sessionEncryption.seed to a long random value. This stops forging, but a leaked session ID can still be reused by another user until you run 1.2.0.

Summary

The MCP proxy in Agent Router gives each client an encrypted Mcp-Session-Id. Inside it, the gateway stores the route, the backend MCP session IDs and the caller's subject (the JWT sub claim). The subject is there to stop session hijacking, as the MCP security best practices ask. But before 1.2.0 the resume path threw the subject away and never compared it with the current caller. On top of that, the official Helm chart shipped the public value default-insecure-seed as the key seed, so anyone could decrypt observed session IDs and build new ones.

Affected versions

Agent Router, formerly Envoy AI Gateway (Go module github.com/envoyproxy/ai-gateway, Helm chart ai-gateway-helm), 1.1.0 and earlier, with an MCPRoute in use. Fixed in 1.2.0 (2026-10-07). The cross-user risk matters most on routes with an OAuth/JWT securityPolicy, where several users share one gateway. Routes without OAuth have no subject, so their sessions were never tied to a user. Reviewed at commit 472da7d (2026-09-10).

Impact

  • (a) No subject check on resume. Any authenticated user who got hold of another user's Mcp-Session-Id (from logs, a proxy, a shared tool, a browser) could send it with their own token. The gateway decrypted it and forwarded the request on the other user's already-initialized backend MCP sessions: tools/call, resources/read and other JSON-RPC methods ran in that user's context.
  • (b) Public default seed. The AES-GCM key is derived with PBKDF2 from the seed and a salt that travels inside the session ID. With the chart default default-insecure-seed (and the default 100,000 iterations), anyone can decrypt an observed session ID, read the backend session IDs and subject inside, and encrypt a new one for any subject and backend session they choose.
  • (c) Both together. With the default seed, the subject check alone is not enough: an attacker can decrypt a captured session ID and re-encrypt it with their own subject. That is why 1.2.0 fixes both parts.

On a multi-tenant gateway this crosses the exact user boundary the subject was added to protect. A working exploit is withheld.

Affected code

Permalinks at 780fcf4, the parent of the fix commit.

Fix

  • #2740 (commit 0d1e730, by the maintainer): backendSessionIDs() now returns the embedded subject, and sessionFromID rejects the request with 401 when it does not match the current caller's verified subject. It also adds a startup warning when the extproc runs with the public default seed.
  • #2755 (commit 9945796, written by Aeon): when seed is empty (the new default), the chart generates a random 64-character seed on install, stores it in the Secret <controller fullname>-mcp-session-encryption, and reuses it on upgrade. A new existingSecret value supports helm template setups.
  • Both are in 1.2.0, and both are still present on main at f4571d6 (2026-10-09).

Detection (for defenders)

  • Check whether you ran with the public seed: look for --mcpSessionEncryptionSeed=default-insecure-seed in the controller Deployment, or -mcpSessionEncryptionSeed default-insecure-seed in the extproc container args of your gateway pods. If so, treat any MCP session ID that was ever exposed as readable by others.
  • On 1.2.0, the extproc logs a warning that the seed is the well-known default if it still is, and resume attempts with a session ID issued to another subject fail with 401 and session ID was issued for a different authenticated subject.
  • Before 1.2.0, if your access logs record both the Mcp-Session-Id and the JWT subject, look for one session ID used with more than one subject.

Timeline

  1. Reported privately via GitHub PVR (GHSA-953h-535j-jg2v), with a suggested fix. Other researchers reported the same issue in parallel.
  2. Maintainer opens #2740 (subject check on resume).
  3. #2740 merged. The seed part, split out as agreed in the advisory discussion, is merged the same day as #2755, written by Aeon.
  4. Agent Router 1.2.0 released with both fixes; the maintainer publishes GHSA-953h-535j-jg2v.
  5. Public write-up. We asked the maintainer to request a CVE from GitHub in #2828.

Credit

Finder: Aaron Elijah Mars of Aeon. Tool: Aeon.

Also credited on the advisory as reporters: PraveenKumarInjam and kanywst. Fix in #2740 by nacx.

References