CVE-2024-9264 — Grafana SQL Expressions command injection (RCE)
Grafana's experimental SQL Expressions feature (introduced in 11.0) used DuckDB to evaluate queries but left the engine's default trusted extensions enabled — including the system() function. Any authenticated user with the Viewer role or higher could execute arbitrary shell commands on the Grafana host. In homelab installs that typically run Grafana as root in Docker, that's root-level RCE behind a login form many people leave open.
TL;DR
- Critical-severity bug in Grafana's SQL Expressions evaluator (NVD scores 8.8; Grafana Labs scores 9.9 with S:C), disclosed 2024-10-17 in GHSA-99rg-6q39-mwm6.
- Viewer role is enough — no admin, no editor, no API token. Any login that can reach the data-source query path.
- Effective impact: shell on the Grafana host. Root if Grafana runs as root (the default in many homelab Docker compose files).
- Affected: Grafana 11.0.0–11.0.5, 11.1.0–11.1.6, 11.2.0–11.2.1. Fixed in 11.0.6, 11.1.7, 11.2.2 (and 11.3.0 by default).
- Workaround if patching is delayed: disable the
sqlExpressionsfeature toggle ingrafana.ini. - Public PoC landed within 48 hours of disclosure. Homelab installs that expose Grafana to the LAN or the open internet should treat this as urgent.
At a glance
| CVE ID | CVE-2024-9264 |
|---|---|
| Severity | High (NVD CVSS 8.8) / Critical (Grafana Labs CVSS 9.9 with S:C) |
| CVSS vector (NVD) | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-94 (Improper Control of Generation of Code) · CWE-77 (Command Injection) |
| Advisory | GHSA-99rg-6q39-mwm6 |
| Published | 2024-10-17 |
| Affected versions | Grafana 11.0.0–11.0.5, 11.1.0–11.1.6, 11.2.0–11.2.1 |
| Fixed versions | 11.0.6, 11.1.7, 11.2.2 (and 11.3.0+) |
| Impact | RCE on Grafana host as the Grafana process user |
What goes wrong in SQL Expressions
SQL Expressions is an experimental feature added in Grafana 11.0 that lets dashboard authors run SQL on top of arbitrary data-source results, using DuckDB as the embedded engine. The intent is to give analysts a "join two Prometheus queries and pivot" escape hatch without leaving the dashboard.
DuckDB ships with a set of optional extensions that are marked
trusted by default — httpfs,
parquet, and crucially the system extension which
exposes a system() table function that runs shell
commands on the host. DuckDB assumes the caller controls the SQL
it's running; that assumption is fine for an analytics CLI, but
not for a multi-user web application.
Grafana's SQL Expressions evaluator instantiated DuckDB without
restricting which extensions could be installed or loaded.
INSTALL and LOAD were reachable from
user-supplied SQL, and once system() was available,
the query result included whatever the spawned command wrote to
stdout. Because evaluation happens inside the Grafana backend
process, every shell call inherits Grafana's user, its
filesystem view, and its network position behind whatever firewall
protects the host.
The authentication bar is the lowest meaningful one Grafana has: the Viewer role. Viewer is the role you grant to "people who should be able to look at dashboards"; it does not grant data-source editing, alerting, or admin. SQL Expressions evaluation runs as part of the query pipeline that Viewer is allowed to drive, which is how a read-only role became a remote shell.
Affected versions and fix paths
Match your installed Grafana version against the vulnerable column; upgrade to the fixed column on the same release line, or move to 11.3+.
| Release line | Vulnerable | Fixed in |
|---|---|---|
| Grafana 11.0.x | 11.0.0 – 11.0.5 | 11.0.6 |
| Grafana 11.1.x | 11.1.0 – 11.1.6 | 11.1.7 |
| Grafana 11.2.x | 11.2.0 – 11.2.1 | 11.2.2 |
| Grafana 11.3+ | Not affected — feature gated by default | n/a |
| Grafana 10.x and earlier | Feature did not exist — not affected | n/a |
Quick scan check
Run one of these on each host that runs Grafana. The goal is to
identify the running version and whether the
sqlExpressions feature toggle is enabled.
# Docker container
docker exec grafana grafana-server -v
# Bare metal (systemd / Debian / Ubuntu)
grafana-server -v
dpkg -l grafana 2>/dev/null | awk '/^ii/ {print $2, $3}'
# Bare metal (RHEL family)
rpm -q grafana
# Over the network (no shell required)
curl -s http://your-grafana:3000/api/health
# Check whether the vulnerable feature is enabled
grep -E 'sqlExpressions|feature_toggles' /etc/grafana/grafana.ini
If the version starts with 11.0., 11.1. or 11.2. and is below the matching fix in the table above, the host is exposed whenever the SQL Expressions toggle is enabled. The /api/health endpoint returns a JSON object with the version string and does not require authentication on most installs.
Mitigations beyond patching
- Disable the feature toggle. In
grafana.ini, removesqlExpressionsfrom[feature_toggles] enable =and restart Grafana. This shuts the door without waiting for a packaged update. - Audit who can log in. Many homelab Grafana installs allow self-service signup or run with a shared "homelab" account that everyone on the LAN knows. Either is enough for this CVE. Disable
allow_sign_upin[users], and rotate any shared credential. - Run Grafana as non-root. The official Docker image uses UID 472 — make sure your compose file isn't overriding that with
user: rootor a bind-mounted config file that forces it. RCE asgrafanais bad; RCE as root is worse. - Network-isolate the Grafana host. Grafana sits in front of every metric you collect, which means the Grafana host already has read access to most of your monitoring infrastructure. Don't compound that by leaving SSH, Docker socket, or Kubernetes API reachable from the same network segment.
- Don't expose Grafana to the open internet — see before you expose a service to the web for the checklist that would have caught this class of bug.
Why this matters more in homelabs than enterprises
Enterprise Grafana installs almost always sit behind SSO, with role assignments synced from an IdP and network reachability restricted to a corporate VPN. Compromising a Viewer account requires getting past the IdP, and even then the Grafana host usually lives in a segment with no outbound internet and a restrictive egress policy.
Homelab Grafana is a different shape. It's typically on the
shared homelab LAN, reachable from anything else on that LAN —
including a guest WiFi if the segmentation is loose. Auth is
often a single shared password, sometimes the default
admin / admin from the initial setup that never
got changed. Many self-hosters expose Grafana through a reverse
proxy with no extra auth layer because it has its own login.
That model is fine when the login is the only attack surface —
it stops working the moment a Viewer account is sufficient for
RCE.
The result is a population where the patching gap and the auth-strength gap multiply: see the patching gap on self-hosted services for how long these windows tend to stay open in practice.
What Noxen does about this
Noxen flags Grafana's web UI as an exposed admin surface whenever it's reachable on a host you've enrolled, and matches CVE-2024-9264 against the installed Grafana package version across your fleet. If a host runs Grafana 11.0.0–11.2.1 below the matching fix line, you get a finding with the affected version, the fix version, and the upgrade path — alongside the admin-surface flag that tells you whether the login page is actually reachable.
Authoritative sources
- NVD entry for CVE-2024-9264
- cve.org record
- GHSA-99rg-6q39-mwm6 — Grafana security advisory
- FIRST EPSS API for CVE-2024-9264
See what Noxen does about CVEs like this → Back to the CVE feed →