{"id":"CVE-2026-44428","aliases":["GHSA-95c3-6vvw-4mrq","GO-2026-5273"],"url":"https://o3.security/vulnerability/CVE-2026-44428","summary":"MCP Registry: GitHub OIDC tokens replayable across registry deployments due to shared audience","details":"# [SECURITY] registry_001 Vulnerability Report\n\nWhile analyzing the code logic, an area that may lead to unintended behavior under specific conditions was discovered. \n\n## Overview\n- Verified Version: `c5c4b9e8890dd5754bee889b2f1417f4fe3b5ce5`\n- Vulnerability Type: Authentication bypass via cross-registry OIDC token replay\n- Affected Location: `cmd/publisher/commands/login.go:67-105,130-135,199-224`; `cmd/publisher/auth/github-oidc.go:24-38,58-75,108-165`; `internal/api/handlers/v0/auth/github_oidc.go:75-135,229-277,280-296`\n- Trigger Scenario: a workflow invokes `mcp-publisher login github-oidc --registry <other-registry>` (or equivalent publish flow) and the publisher still requests a GitHub Actions ID token with the shared audience `mcp-registry`; any other registry deployment running this code can replay that token to its own `/v0/auth/github-oidc` endpoint and mint a publish-capable registry JWT for the same GitHub owner namespace.\n\n## Root Cause\nThe client-side and server-side GitHub OIDC flow is bound only to a global audience string, not to the specific registry instance being targeted. On the client side, the publisher always appends `audience=mcp-registry` when requesting the GitHub Actions ID token, regardless of the selected `--registry` URL. On the server side, the exchange endpoint validates only that same fixed audience and then derives publish permissions directly from `repository_owner`. As a result, a token legitimately obtained while interacting with one registry deployment remains acceptable to any other deployment that shares the same code and audience string.\n\n## Source-to-Sink Chain\n1. Source\n   `cmd/publisher/commands/login.go:67-105,130-135,199-224` parses the user-controlled `--registry` flag into `flags.RegistryURL`, creates a `GitHubOIDCProvider`, and calls `authProvider.GetToken(ctx)` for the chosen authentication method.\n2. Propagation\n   `cmd/publisher/auth/github-oidc.go:24-38` obtains an OIDC token and immediately exchanges it against the selected registry URL.\n   `cmd/publisher/auth/github-oidc.go:58-75` builds `exchangeURL := o.registryURL + \"/v0/auth/github-oidc\"` and posts the GitHub token to whichever registry instance was selected.\n   `cmd/publisher/auth/github-oidc.go:108-165` constructs `fullURL := requestURL + \"&audience=mcp-registry\"` and therefore requests the same audience for every registry deployment.\n3. Sink\n   `internal/api/handlers/v0/auth/github_oidc.go:75-135` validates only the shared audience value passed into `ValidateToken`.\n   `internal/api/handlers/v0/auth/github_oidc.go:254-277` calls `h.validator.ValidateToken(ctx, oidcToken, \"mcp-registry\")` and, on success, signs a new registry JWT.\n   `internal/api/handlers/v0/auth/github_oidc.go:280-296` converts `claims.RepositoryOwner` into the publish permission pattern `io.github.<owner>/*`, which is then embedded into the new registry JWT.\n\n## Exploitation Preconditions\n1. The victim uses the GitHub Actions OIDC publishing path.\n2. The victim workflow targets another registry deployment first, such as staging, self-hosted infrastructure, or an attacker-controlled registry URL.\n3. The receiving registry deployment can observe the posted OIDC token and replay it before expiry to another registry deployment running the same shared audience configuration.\n\n## Risk\nThis breaks deployment isolation between registry instances. A token issued for one registry interaction can be replayed across trust boundaries, allowing one deployment to impersonate the same GitHub owner identity on another deployment.\n\n## Impact\nAn attacker-controlled or compromised registry deployment can mint a valid registry JWT on another deployment and inherit publish permissions for the victim GitHub owner namespace. In practical terms, this enables unauthorized publication or update actions for names such as `io.github.<owner>/*` on the victim registry instance.\n\n## Remediation\n1. Replace the shared audience string with a registry-specific audience, such as a deployment-specific client ID or origin-derived identifier.\n2. Ensure the publisher requests the audience that matches the exact registry instance it is targeting, and ensure the server validates that same instance-specific value.\n3. Consider binding the exchange to additional deployment-specific claims so that a token captured by one registry cannot be replayed on another.\n4. Add regression tests that cover cross-deployment replay attempts between different registry URLs.","published":"2026-05-14T21:09:10.092Z","modified":"2026-08-12T03:51:45.141361880Z","cvss":null,"epss":{"score":0.00219,"percentile":0.12165,"asOf":"2026-08-24"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"Go","name":"github.com/modelcontextprotocol/registry","fixedVersion":"1.7.6"}],"fix":{"url":"https://github.com/modelcontextprotocol/registry/pull/1229","label":"modelcontextprotocol/registry#1229"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44428.json"},{"type":"ADVISORY","url":"https://github.com/modelcontextprotocol/registry/security/advisories/GHSA-95c3-6vvw-4mrq"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-44428"},{"type":"WEB","url":"https://github.com/modelcontextprotocol/registry/pull/1229"},{"type":"WEB","url":"https://github.com/modelcontextprotocol/registry/commit/3f89fc2b1fb34fd49f3c0e1b39e964a5c67b613f"},{"type":"PACKAGE","url":"https://github.com/modelcontextprotocol/registry"},{"type":"WEB","url":"https://github.com/modelcontextprotocol/registry/releases/tag/v1.7.6"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:45.141361880Z"}}