← Back to Feed CACHED · 2026-07-23 03:24:28 · CACHE_KEY tenable:297198
tenable:297198 · CWE-833 · Disclosed 2026-01-27

Grafana Labs 3

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
Do you agree?
01 · The Real Story

A leaky faucet in Grafana's avatar plumbing that eventually floods the basement

CVE-2026-21720 is a goroutine leak in Grafana's /avatar/:hash endpoint. When a user avatar isn't cached, Grafana spawns a background goroutine to fetch it from Gravatar. That goroutine sits in a 10-slot worker queue; if it waits longer than three seconds, the HTTP handler times out and stops listening — but the goroutine keeps running and tries to send its result on an unbuffered channel with no receiver, so it blocks forever. Every request with a fresh random hash leaks one goroutine and its associated memory. Sustained traffic exhausts memory and crashes the process. Affected: Grafana 3.0.011.6.8, 12.0.012.0.7, 12.1.012.1.4, 12.2.012.2.2, and 12.3.0.

Vendor severity is HIGH / 7.5 because the CVSS vector (AV:N/AC:L/PR:N/UI:N/A:H) rewards unauthenticated network reachability and full availability impact. That math is technically correct but ignores the real world: Grafana is overwhelmingly an internal service (SSO'd dashboards for eng/SRE), the impact is *just* DoS (no confidentiality or integrity loss), and the exploit is a slow-burn memory pressure attack, not an instant crash. HIGH overstates the operational reality for the ~95% of installs behind a VPN or reverse proxy.

"Goroutine leak in Grafana's Gravatar handler. DoS-only, internal-facing service, no RCE — downgrade to MEDIUM."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach the /avatar/:hash endpoint

Attacker needs HTTP reachability to the Grafana web listener. The /avatar/:hash route is served before authentication on most builds so login pages can render user avatars, meaning no credentials are needed.
Conditions required:
  • Network path to Grafana HTTP(S) port (typically 3000)
  • Endpoint not blocked by upstream WAF/reverse proxy
Where this breaks in practice:
  • Grafana is rarely internet-exposed — most installs sit behind SSO, corporate VPN, or an ingress with auth
  • Shodan shows a small fraction of Grafana instances directly on the public internet
Detection/coverage: Any web scanner (Nessus 297198, Qualys) fingerprints Grafana version banner via /api/health
STEP 02

Send a flood of unique avatar hashes

Attacker crafts requests like GET /avatar/<random-md5> at high rate. Each unique hash bypasses Grafana's avatar cache and forces a new goroutine to be enqueued. Trivially scriptable with curl, hey, or wrk — no exploit code required.
Conditions required:
  • Sufficient request throughput to fill the 10-slot queue faster than Gravatar responds
Where this breaks in practice:
  • Rate-limiting reverse proxies (nginx limit_req, Cloudflare, Traefik middleware) neutralize this immediately
  • Egress firewalls blocking outbound to gravatar.com cause faster fetch failures, actually accelerating the leak — a wrinkle
Detection/coverage: Anomalous rate on /avatar/* shows up in access logs and any RUM/APM tool
STEP 03

Trigger the goroutine leak

For each request that sits in the queue >3s, the handler times out and returns. The goroutine survives and blocks forever on an unbuffered result channel that no one is reading. Memory attributable to those goroutines (stacks + closure state + partial responses) accumulates monotonically.
Conditions required:
  • Grafana's default worker queue depth of 10
  • Gravatar latency or upstream failure > 3s
Where this breaks in practice:
  • Not applicable — this is the vulnerable code path itself
Detection/coverage: Prometheus go_goroutines metric on Grafana's own /metrics climbs linearly; RSS grows without bound
STEP 04

Exhaust process memory → crash / OOMKill

Eventually Grafana's memory footprint exceeds cgroup limits or host RAM. In Kubernetes the pod is OOMKilled; on a VM the OOM killer takes it out or the process panics. Dashboards go dark, alerting pipelines that depend on Grafana rendering (image renderer, unified alerting) degrade.
Conditions required:
  • No memory ceiling / auto-restart that outpaces the leak
  • No horizontal replication to absorb the outage
Where this breaks in practice:
  • HA Grafana behind a load balancer just means all replicas leak in parallel — not real friction, more like a slower fuse
  • Kubernetes liveness restarts contain the blast but don't stop the DoS
Detection/coverage: Container restart counters, kube_pod_container_status_restarts_total, incident channels lighting up
03 · Intelligence Metadata

The supporting signals.

CVECVE-2026-21720
CWECWE-833 (Deadlock) / CWE-400 (Uncontrolled Resource Consumption)
In-the-wild exploitationNone observed. Not on CISA KEV. No reports on GreyNoise tagged to this CVE.
Public PoCNone weaponized. The bug is trivially reproducible with a shell one-liner (for i in $(seq 1 100000); do curl -s http://target/avatar/$(openssl rand -hex 16) & done) — no exploit kit needed.
EPSSCurrently <0.1% (near-floor) — consistent with a DoS bug in an internal-facing dashboard tool
KEV statusNot listed
CVSS v3.0 vectorAV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H7.5 HIGH. Note the C:N/I:N — availability only.
Affected versions3.0.0 – 11.6.8, 12.0.0 – 12.0.7, 12.1.0 – 12.1.4, 12.2.0 – 12.2.2, 12.3.0
Fixed versions11.6.9+security-01, 12.0.8+security-01, 12.1.5+security-01, 12.2.3+security-01, 12.3.1+security-01
Exposure telemetryShodan product:Grafana returns roughly ~110K hosts globally; most are behind auth pages. Direct /avatar/* reachability without auth is common but the population that matters (public internet) is small.
Disclosed2026-01-27 by Grafana Labs Security
ReporterGrafana Labs internal security team
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.3/10)

The single decisive factor is impact ceiling: availability-only DoS on a monitoring service, with no confidentiality or integrity loss and no lateral movement primitive. Grafana is not a role-multiplier component (it does not hold identity, secrets, hypervisor, or data-tier authority), so even a successful crash is bounded to dashboard downtime on one service.

HIGH Impact ceiling is DoS-only (CVSS vector confirms C:N/I:N)
HIGH No known in-the-wild exploitation
MEDIUM Real-world exposed population (public-facing Grafana with pre-auth avatar route)

Why this verdict

  • Impact ceiling is DoS. The CVSS vector is C:N/I:N/A:H — attacker gets a crashed dashboard, not a shell, not creds, not data. That alone caps operational severity below HIGH for most environments.
  • Exposure narrows further. Grafana is predominantly an internal / VPN-gated service. The subset of instances directly reachable on the internet with the pre-auth /avatar/* path exposed is the actual target population — a minority of installs.
  • Role multiplier: low. Grafana is a visualization tier — (a) low-value role: dev/test dashboards, (b) typical role: production observability UI, (c) high-value role: on-call alert rendering / unified alerting. Even in the high-value role, crash impact is *degraded visibility*, not identity or data compromise. No canonical Grafana deployment ends in fleet compromise from this bug. Floor is MEDIUM.
  • Trivial mitigation. A one-line nginx limit_req or a WAF rule on /avatar/* kills the exploit outright — no patching pressure equivalent to a memory-corruption bug.
  • No exploitation signal. Not on KEV, EPSS near-floor, no public exploit tooling. Attackers optimize for RCE/creds, not DoSing someone's Grafana.

Why not higher?

HIGH is reserved for bugs where the blast radius plausibly reaches a domain, fleet, tenant, or supply chain. A goroutine leak that crashes a monitoring UI does not. The vendor's 7.5 is a mechanical CVSS calculation that treats "process crash" as high impact regardless of the process's role.

Why not lower?

Not LOW because unauthenticated attackers can trigger it with zero skill, and losing observability during an active incident has real operational cost (alert blindness). Not IGNORE because internet-exposed Grafana instances (a minority but non-trivial population) are genuinely at risk of hostile DoS during opportunistic scanning.

05 · Compensating Control

What to do — in priority order.

  1. Rate-limit /avatar/* at the reverse proxy — Add nginx limit_req_zone / Traefik RateLimit middleware / Cloudflare rule capping /avatar/* to a few requests per second per IP. This starves the goroutine leak before it can accumulate. No mitigation SLA for MEDIUM under noisgate — apply within the 365-day remediation window, but if the Grafana instance is internet-facing, treat this as a same-week task.
  2. Verify Grafana is not internet-exposed — Confirm all Grafana ingress points require SSO / VPN / IP allowlist before hitting the app. Anonymous access to /avatar/* is what makes this exploitable pre-auth. Audit reverse proxy configs and cloud load balancer listeners this month.
  3. Set memory limits + auto-restart — In Kubernetes, ensure Grafana pods have resources.limits.memory set and a liveness probe that restarts on hang. On VMs, run Grafana under systemd with MemoryMax= and Restart=always. Turns a full outage into a brief blip while you patch.
  4. Alert on go_goroutines growth — Grafana exposes /metrics. A Prometheus rule alerting when go_goroutines{job="grafana"} grows monotonically over 30 min catches this bug being exploited AND catches other future goroutine leaks.
  5. Patch to a +security-01 release on the 365-day remediation window — Track upgrade to 11.6.9+security-01, 12.0.8+security-01, 12.1.5+security-01, 12.2.3+security-01, or 12.3.1+security-01. Because 12.0.x is EOL, plan a version-line migration to 12.3.x or the current LTS at the same time — noisgate remediation SLA for MEDIUM is ≤ 365 days.
What doesn't work
  • Authentication won't save you/avatar/:hash is deliberately served before login so avatar images render on the login page; requiring auth on the dashboard doesn't gate this endpoint.
  • Egress blocking of gravatar.com makes it WORSE — a firewalled egress to Gravatar means the fetch fails slowly, which is exactly the condition (>3s wait) that triggers the leak.
  • Restarting Grafana on a cron buys hours but is not mitigation; a modest attacker can leak enough memory between restarts to cause repeated outages during business hours.
  • A WAF signature for 'Grafana exploit' — there is nothing anomalous in the request payload. Only volumetric rate-limiting on the path works.
06 · Verification

Crowdsourced verification payload.

Run on the Grafana host (or any host with SSH access to it) as an unprivileged user. Invoke as ./check_grafana_cve_2026_21720.sh http://localhost:3000 — no root needed. Script queries the Grafana health endpoint for its version and compares against the fixed set.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_grafana_cve_2026_21720.sh
# Detects CVE-2026-21720 (Grafana avatar goroutine-leak DoS)
# Usage: ./check_grafana_cve_2026_21720.sh <grafana_base_url>
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -u
BASE="${1:-http://localhost:3000}"

JSON=$(curl -fsS --max-time 5 "${BASE%/}/api/health" 2>/dev/null) || {
  echo "UNKNOWN: could not reach ${BASE}/api/health"
  exit 2
}

VER=$(echo "$JSON" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
if [ -z "$VER" ]; then
  echo "UNKNOWN: could not parse version from /api/health"
  exit 2
fi

echo "Detected Grafana version: $VER"

# Strip +security-01 suffix marker before numeric compare
CORE=$(echo "$VER" | cut -d+ -f1)
SUFFIX=$(echo "$VER" | grep -o '+security-[0-9]*' || true)

MAJ=$(echo "$CORE" | cut -d. -f1)
MIN=$(echo "$CORE" | cut -d. -f2)
PAT=$(echo "$CORE" | cut -d. -f3)

vuln() { echo "VULNERABLE: $VER is affected by CVE-2026-21720"; exit 1; }
safe() { echo "PATCHED: $VER contains the CVE-2026-21720 fix";   exit 0; }

# Fixed floors per line:
#   11.6.9+security-01, 12.0.8+security-01, 12.1.5+security-01,
#   12.2.3+security-01, 12.3.1+security-01, >= 12.4.0
case "$MAJ.$MIN" in
  11.6)
    if [ "$PAT" -gt 9 ] || { [ "$PAT" -eq 9 ] && [ -n "$SUFFIX" ]; }; then safe; else vuln; fi ;;
  12.0)
    if [ "$PAT" -gt 8 ] || { [ "$PAT" -eq 8 ] && [ -n "$SUFFIX" ]; }; then safe; else vuln; fi ;;
  12.1)
    if [ "$PAT" -gt 5 ] || { [ "$PAT" -eq 5 ] && [ -n "$SUFFIX" ]; }; then safe; else vuln; fi ;;
  12.2)
    if [ "$PAT" -gt 3 ] || { [ "$PAT" -eq 3 ] && [ -n "$SUFFIX" ]; }; then safe; else vuln; fi ;;
  12.3)
    if [ "$PAT" -gt 1 ] || { [ "$PAT" -eq 1 ] && [ -n "$SUFFIX" ]; }; then safe; else vuln; fi ;;
  *)
    if [ "$MAJ" -gt 12 ] || { [ "$MAJ" -eq 12 ] && [ "$MIN" -ge 4 ]; }; then safe; fi
    if [ "$MAJ" -lt 11 ] || { [ "$MAJ" -eq 11 ] && [ "$MIN" -lt 6 ]; }; then vuln; fi
    echo "UNKNOWN: version $VER is outside the known matrix — verify manually"
    exit 2
    ;;
esac
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: (1) inventory your Grafana fleet with the verification script and separate internet-facing from VPN-gated instances. (2) For any internet-facing Grafana, drop an nginx / WAF rate limit on /avatar/* today — this neutralizes the exploit regardless of Grafana version. (3) Because noisgate reassesses this as MEDIUM, there is no mitigation SLA — go straight to the 365-day remediation window for internal-only instances, but shorten it to ≤ 30 days for anything reachable from the internet. (4) The noisgate remediation SLA target is ≤ 365 days: fold the upgrade to 11.6.9+security-01 or the appropriate +security-01 build (or 12.4.x if migrating off the EOL 12.0 line) into your normal Grafana maintenance train. Do not lose sleep over this one — it is a memory leak that crashes a dashboard, not a foothold.

Sources

  1. Tenable Plugin 297198
  2. Grafana Labs Security Advisories
  3. Grafana Security Release Blog (Jan 2026)
  4. CVE Details — Grafana
  5. Grafana CVE curated list (kh4sh3i)
  6. Canadian Centre for Cyber Security — Grafana AV26-285
  7. OpenCVE — Grafana product page
  8. Grafana Labs — version list
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.