A New PoC for Grafana’s MCP Server Tells Defenders the Wrong Version Is Safe
A public proof-of-concept for CVE-2026-15583 — the confused-deputy flaw in Grafana MCP Server that leaks a service-account token to any host an unauthenticated caller names — was pushed to GitHub in the early hours of 26 September 2026. It is a careful piece of work: a documented call chain, a localhost-only lab, a benign witness string instead of a payload, and an explicit list of outcomes that do not count as success. It is also wrong about the one field defenders will copy first. Its summary table reads “Affected: all versions through 0.17.0 (inclusive)” and “Patched: 0.17.1 and later.”
Grafana, which is the CNA for its own record, says otherwise. The NVD entry for CVE-2026-15583 — published 15 July 2026, still Awaiting Analysis — carries vendor-supplied affected data of 0.0.0 through 0.17.1 inclusive, semver range, everything else unaffected. The fix is not in 0.17.1. It is in 0.17.2.
The commit history settles it
The repository makes the timeline unambiguous. v0.17.0 shipped 23 June. v0.17.1 shipped 7 July, and its changelog does contain a Security heading — “Block DNS rebinding attacks on the HTTP and SSE transports” (PR #957), plus an unrelated deeplink fix. That is a different vulnerability class in the same transports, and it is precisely the sort of thing that makes an off-by-one look plausible: the release a defender would skip is a security release, just not this one.
v0.17.2 shipped 13 July — two days before the CVE went public — and it is the release that closes this hole. Comparing the two tags returns four commits, of which one is “fix(security): bind environment credentials to the configured Grafana URL” (PR #995, merged 13 July, touching mcpgrafana.go, client_cache.go, and adding 88 lines of tests). The pull request explains the bug better than the CVE text does: on the HTTP and SSE transports, the Grafana URL and the credentials were resolved from the request independently, each with its own fallback. Supply an X-Grafana-URL header and omit the credential header, and the server would dial the caller-specified host while still attaching the environment-configured service-account token. The patch binds environment credentials to the environment-configured URL: if X-Grafana-URL points somewhere else, the token, the deprecated API key, basic auth, and extra headers are all withheld.
So the PoC's own description of the flaw is accurate, its call chain is accurate, and its remediation floor is one version too low. A team that treats the PoC as the authoritative artifact — which is what happens when a scanner rule or a triage ticket gets written from a GitHub README instead of the vendor record — marks a vulnerable deployment as remediated and closes the finding.
Why this particular bug punishes a one-version error
The impact is not a crash or a read primitive. It is credential relocation. Grafana rates it 8.6 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N) — network reachable, no privileges, no user interaction, and scope changed, which is the vector acknowledging that the damage lands somewhere other than the MCP server. What the attacker collects is a live Grafana service-account token; what they do with it is bounded only by that account's permissions on the real instance. The NVD description adds the second half: the same header primitive gives SSRF against arbitrary internal services, including cloud metadata endpoints. One unauthenticated request, two distinct outcomes.
CISA's SSVC assessment recorded on the same day called exploitation none and automatable yes. The automatable half was always the important one — there is no exploit development involved here, just a header — and the exploitation half is the field that a published PoC is designed to change. Note also a small taxonomy split worth knowing about if you pivot on weakness IDs: NVD's secondary weakness entry is CWE-610 (externally controlled reference to a resource in another sphere), while the PoC labels it CWE-918 (SSRF). Both describe something real, but they will not match each other in a dashboard filter.
This is the same failure shape this site has documented repeatedly across MCP implementations: the server holds a high-value credential on behalf of a user it never authenticates, and some field of the incoming request gets to influence where that credential is sent. Compare mcp-atlassian's unauthenticated file exfiltration, LiteLLM's MCP auth bypass now sitting in CISA's KEV catalog, and DBHub's DNS-rebinding trio. Grafana's own 0.17.1 DNS-rebinding fix belongs to that family too — which is to say this project patched two separate header-trust bugs in the same transports six days apart.
The wider lesson: PoC metadata is not advisory data
There is a real service being performed by researchers who publish structured, non-weaponised reproductions with explicit non-goals. The artifact in question refuses to dress itself up: no reverse shell, no eval, a lab pinned to 127.0.0.1, and an honest statement that a patched target is not a success condition. The problem is narrower and more mundane — the summary table in a README is hand-written prose, generated at whatever moment the author last read the record, and it inherits no correction when the vendor revises the affected range. Advisory databases are structured for a reason.
Treat every third-party version table the same way: as a hypothesis to check against the CNA record and the commit log, never as the input to a remediation decision. In this case the check takes under a minute, and the two sources disagree.
What to do
- Treat 0.17.1 as vulnerable, not fixed. The vendor-supplied affected range in the CVE record runs through 0.17.1 inclusive; the binding fix is in 0.17.2. If a ticket was closed on the strength of “patched: 0.17.1,” reopen it.
- Move to a current release rather than the minimum. The project is well past this line — v1.6.0 shipped 25 September — and 0.17.2 also carried a dependency bump for a separate LiteLLM CVE. There is no reason to sit on the patch floor.
- Rotate the service-account token if an exposed instance ran below 0.17.2. Exfiltration leaves no trace on the MCP server worth finding; assume disclosure rather than trying to prove it. Scope the replacement token to the minimum the agent workflow actually needs.
- Hunt for
X-Grafana-URLin front-proxy and gateway logs. Requests to/mcpor/ssecarrying that header with an off-instance host are the signature. Strip caller-supplied instance-URL headers at the edge as a standing control. - Stop exposing MCP transports to untrusted networks. Every entry in this bug class assumes the attacker can reach the HTTP or SSE endpoint unauthenticated — see the 15,465-server governance survey for how routinely that assumption holds.
Sources:
- NVD — CVE-2026-15583 (published 15 July 2026, CNA Grafana; affected 0.0.0 through 0.17.1 inclusive; CVSS 3.1 base 8.6 High; CWE-610; CISA SSVC exploitation none / automatable yes)
- Grafana Labs — security advisory for CVE-2026-15583 (reference of record in NVD)
- grafana/mcp-grafana PR #995 — “fix(security): bind environment credentials to the configured Grafana URL,” merged 13 July 2026
- grafana/mcp-grafana — v0.17.2 release notes (13 July 2026), the release carrying the credential-binding fix
- grafana/mcp-grafana — v0.17.1 release notes (7 July 2026), the separate DNS-rebinding fix (PR #957)
- abraxas/CVE-2026-15583 — proof-of-concept repository created 26 September 2026, stating “Patched: 0.17.1 and later”