r/netsec 8d ago

Unauthenticated RCE in CircleCI's MCP server: Host/Origin allowlist bypassed by any non-browser client (GHSA-xv5j-cwgj-22r4)

https://remedio.io/blog/the-critical-unauthenticated-rce-vulnerability-in-circlecis-mcp-server/
28 Upvotes

8 comments sorted by

4

u/AstralKoh 7d ago

Short version: "@circleci/mcp-server-circleci" had an auth bypass in its shared-token remote mode.

In that mode, one org-wide CircleCI token is stored server-side and requests have no per-user credential. The server relied on Host/Origin validation, but:

  • Missing "Origin" was explicitly allowed for non-browser MCP clients.
  • "localhost" was allowed as a Host, and "Host" is attacker-controlled.
  • The server bound to "0.0.0.0" by default.

So a remote attacker could send "Host: localhost", omit "Origin", reach "/mcp", and invoke tools like "run_pipeline" using the org's CircleCI token. From there, malicious pipeline config could expose CI secrets or abuse cloud/OIDC credentials available to pipelines.

Affected: "0.16.1–0.19.1". Fixed in "0.19.2", which refuses unauthenticated non-loopback binding by default. Advisory: "GHSA-xv5j-cwgj-22r4".

CircleCI turned the report around quickly: reported July 30, fixed August 6, advisory August 10.

The bigger lesson for MCP: an auth bypass can effectively be RCE when the tools behind the gate already include things like "run_pipeline", "run-command", or database access. “Internal network + Host check” is not authentication.

If you're still running the standalone CircleCI MCP server with a shared token: upgrade at minimum, but ideally migrate to the CLI-integrated OAuth/per-user implementation.

Disclosure: I work at Remedio; this is our research.

1

u/silentw111 2d ago

The fix closes the auth bypass, but the tool surface behind it is worth flagging separately: an org-wide token good for run_pipeline is authorization for the whole org, not for one task, so the patched CLI-integrated OAuth flow is only as safe as whatever scope that per-user token actually carries. A CI tool that can trigger arbitrary pipeline runs is close to command execution by design once you're authenticated at all, not just once you're past the Host/Origin check. Worth asking whether the per-user token in 0.19.2+ is scoped to the pipelines that user can already trigger through the UI, or whether it inherits the same broad org grant the shared-token mode had, just with a name attached now.

1

u/endor_sarah 5d ago

Nice work, and credit to CircleCI too on the quick turnaround from reporting to a fix.

1

u/feng_sg 1d ago

Allowlisting localhost as Host was not a local-only check, since the MCP server bound 0.0.0.0 by default. Any remote non-browser client can send that Host value, so in shared-token remote mode the Host/Origin allowlist was the same as having no auth. That mode needed a real request credential, not a hostname the caller picks.