What's the vulnerability?

Grafana v11.6.0+ SQL Expressions feature allows authenticated users (Viewer+) to write arbitrary files via malicious SQL queries. Chain: 1) Login, 2) Craft SQL Expression query, 3) Overwrite Sqlyze driver binary (<v1.5.0) or AWS config, 4) RCE. Prerequisites: sqlExpressions feature toggle enabled, vulnerable plugin.

Root Cause Analysis

# RCA Report: CVE-2026-27876 Grafana SQL Expressions RCE

## Summary

CVE-2026-27876 is a critical vulnerability in Grafana's SQL Expressions feature that allows authenticated users (Viewer+) to achieve arbitrary file write via malicious SQL queries using INTO clauses. The vulnerability exists because the SQL expression parser did not block INTO clauses, allowing attackers to write files to the filesystem. This can be chained with vulnerable Grafana Enterprise plugins (like Sqlyze driver <v1.5.0) to achieve Remote Code Execution (RCE). The vulnerability was introduced in Grafana v11.6.0 and fixed in v11.6.14.

## Impact

- **Package/Component Affected**: Grafana OSS/Enterprise SQL Expressions feature (`pkg/expr/sql`)
- **Affected Versions**: Grafana v11.6.0 through v11.6.13, v12.0.0 through v12.0.x, v12.1.0 through v12.1.9, v12.2.0 through v12.2.7, v12.3.0 through v12.3.5, v12.4.0 through v12.4.1
- **Fixed Versions**: v11.6.14, v12.1.10, v12.2.8, v12.3.6, v12.4.2
- **Risk Level**: Critical (CVSS 9.1)
- **Consequences**: 
  - Arbitrary file write on the Grafana server filesystem
  - Potential Remote Code Execution when chained with vulnerable Enterprise plugins
  - Authentication required: Viewer+ permissions
  - Feature toggle required: `sqlExpressions` must be enabled

## Root Cause

The vulnerability exists in the SQL expression parser (`pkg/expr/sql/parser_allow.go`). The `AllowQuery()` function uses an allowlist approach to validate SQL queries before execution. In vulnerable versions, the `*sqlparser.Into` node type was allowed:

```go
case *sqlparser.Into:
    return  // INTO was allowed - vulnerable!
```

This allowed attackers to use `SELECT ... INTO OUTFILE '/path/to/file'` syntax to write arbitrary files.

The fix in v11.6.14+ blocks INTO clauses:

```go
case *sqlparser.Into:
    // Plain SELECT statements may carry a typed-nil Into pointer.
    // Reject only when INTO is actually present.
    return v == nil  // INTO is now blocked!
```

**Fix Commit**: `4da4079945` - "[release-11.6.14] Apply security patches" which includes "patch(security): block INTO clauses in SQL expression allowlist"

## Reproduction Steps

The reproduction script is located at `repro/reproduction_steps.sh`. It performs the following:

1. **Deploy**: Starts Grafana v11.6.0 Docker container with `sqlExpressions` feature toggle enabled
2. **Health Check**: Waits for Grafana API to be ready (`/api/health`)
3. **Authentication**: Logs in as admin user via `/login` endpoint and obtains session cookie
4. **Feature Verification**: Confirms `sqlExpressions` is enabled via `/api/frontend/settings`
5. **Exploit Attempt**: Sends SQL expression queries with INTO clauses:
   - `SELECT 1 INTO OUTFILE '/tmp/cve_test1.txt'`
   - `SELECT 1 INTO DUMPFILE '/tmp/cve_test2.txt'`
   - Control test: `SELECT 1` (should succeed)
6. **Evidence Collection**: Captures all HTTP responses and checks for file creation

**Expected Evidence**:
- If vulnerable: SQL INTO queries succeed and files are created on the filesystem
- If patched: Queries return errors like "blocked node" or "not supported"

## Evidence

During testing of Grafana v11.6.0 Docker image (grafana/grafana:11.6.0):

**Login Success**: 
- Location: `artifacts/login_response.json`
- Evidence: `{"message":"Logged in","redirectUrl":"/"}`

**SQL INTO Clause Blocked**:
- Location: `artifacts/sql_response_into_outfile.json`
- Evidence: `{"results":{"A":{"error":"failed to parse SQL expression: blocked node sqlparser.Variables - not supported in queries"...}}`

**Control Test (SELECT only) Works**:
- Location: `artifacts/sql_response_select_only.json`
- Evidence: `{"results":{"A":{"status":200,"frames":[{"schema":{"name":"A"...`

**Feature Toggle Enabled**:
- Location: `artifacts/frontend_settings.json`
- Evidence: `"sqlExpressions":true`

**No File Creation**:
- Checked `/tmp/*.txt` files in container - none were created by SQL expressions

## Reproduction Results

**Status**: INCONCLUSIVE/PATCHED

The tested Grafana v11.6.0 Docker image appears to have security controls in place that block INTO clauses. The error message "blocked node sqlparser.Variables" indicates an allowlist is blocking the query structure, even though the v11.6.0 source code shows INTO should be allowed.

**Possible Explanations**:
1. The Docker image was updated with a backported security patch
2. The vulnerability requires specific runtime conditions not met in the test environment
3. The Enterprise plugin component (Sqlyze) is required for the full exploit chain

## Recommendations / Next Steps

**For Users**:
1. **Upgrade immediately** to patched versions: v11.6.14+, v12.1.10+, v12.2.8+, v12.3.6+, or v12.4.2+
2. If unable to upgrade, disable `sqlExpressions` feature toggle
3. Restrict Viewer+ access to trusted users only

**For Security Teams**:
1. Audit Grafana instances for `sqlExpressions` feature toggle status
2. Check for unexpected files in Grafana server directories
3. Monitor for suspicious SQL expression queries in logs

**For Further Testing**:
1. Test with Grafana Enterprise edition and Sqlyze plugin installed
2. Build Grafana v11.6.0 from source code to verify exact vulnerable behavior
3. Test chained exploit with file overwrite → plugin exploitation path

## Additional Notes

**Idempotency**: The reproduction script is idempotent - it cleans up containers on each run.

**Limitations**:
- Testing was performed on Grafana OSS Docker image
- The full RCE chain (file write → plugin exploitation) was not achieved
- The Enterprise plugin component was not tested

**Docker Image Note**: The tested image `grafana/grafana:11.6.0` (digest: sha256:62d2b9d20a19714ebfe48d1bb405086081bc602aa053e28cf6d73c7640dfb) may have been updated since the CVE was published. The observed behavior (INTO clause blocking) suggests the image may include security patches not reflected in its tag.
One Command

Verify with pruva-verify

Run the Pruva CLI to automatically fetch and execute the reproduction script.

pruva-verify REPRO-2026-00125
or pruva-verify CVE-2026-27876
Install: curl -fsSL https://pruva.dev/install.sh | sh

Or Run Manually

1

Download the script

curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00125/artifacts/reproduction_steps.sh
2

Make executable

chmod +x reproduction_steps.sh
3

Run the script

./reproduction_steps.sh
Run in a VM, container, or disposable environment. This exploits a real vulnerability.

How Pruva Reproduced This

Watch the AI agent's step-by-step process.

Loading session...

Artifacts

No artifacts available