CVE-2021-43798 Grafana Directory Traversal Deep Dive

CVE-2021-43798 is a critical unauthenticated directory traversal in Grafana 8.x that allows arbitrary file read through the /public/plugins/ asset endpoint. This post walks through root cause, manual exploitation, the URL normalization trap that breaks most scripts, high-value post-exploitation targets, and scaled testing with GrafTraverse — a purpose-built automation framework for authorized assessments.
01 · Introduction & Vulnerability Overview
::1.1 Why Grafana Matters
Grafana is one of the most widely deployed open-source observability platforms. It aggregates metrics, logs, and traces from databases, cloud APIs, and LDAP directories through a single web interface. That central role makes it a high-value target: one successful file read can expose credentials for every system it monitors.
::1.2 Vulnerability Summary
CVE-2021-43798 is an unauthenticated Local File Inclusion (LFI) / directory traversal flaw affecting Grafana 8.0.0-beta1 through 8.3.0, disclosed in December 2021. Attackers manipulate path segments in plugin asset requests to escape the intended directory and read arbitrary files on the host filesystem. No authentication or special configuration is required — a single crafted HTTP GET is sufficient.
::1.3 Affected Versions
| Branch | Last Vulnerable | First Patched |
|---|---|---|
| 8.3.x | 8.3.0 | 8.3.1 |
| 8.2.x | 8.2.6 | 8.2.7 |
| 8.1.x | 8.1.7 | 8.1.8 |
| 8.0.x | 8.0.6 | 8.0.7 |
| 7.5.x (LTS) | 7.5.11 | 7.5.12 |
Any unpatched 8.x instance prior to the versions above is exploitable with no preconditions.
02 · Root Cause Analysis
::2.1 The Vulnerable Endpoint
Grafana serves plugin static assets via an intentionally unauthenticated route:
Plugins must deliver JS, CSS, and images before a user session exists — so the route is public by design. The failure was missing path confinement after routing.
::2.2 Expected vs Actual Behavior
Expected:
- ▹
Resolve the plugin asset directory.
- ▹
Append the requested file path.
- ▹
Verify the canonical path remains inside the asset directory.
- ▹
Serve the file only if confinement passes.
Actual: Step 3 was absent. Traversal sequences (../) were not neutralized, so the resolver followed them through the filesystem.
Resolves to /etc/passwd — outside the plugin asset tree entirely.
::2.3 Why alertlist?
alertlist is a built-in plugin present in default installations and appears in most public PoCs. Any installed plugin name works; graph, table, prometheus, and loki are common alternatives when alertlist is removed.
03 · Reconnaissance & Target Discovery

Public Grafana dashboards are common in cloud, DevOps, and SOC/NOC environments. Building a target inventory is the first operational step.
::3.1 Shodan Dorks
::3.2 Censys / FOFA
::3.3 Target Inventory Format
Normalize results to one URL per line for batch tooling:
04 · Manual Exploitation
::4.1 The Exploit Request
A vulnerable host returns 200 OK with /etc/passwd contents. Eight ../ segments reliably reach the filesystem root from typical install paths.
::4.2 Plugin Fallback List
If alertlist returns 404, cycle built-in plugins:
::4.3 curl (with --path-as-is)

Without --path-as-is, curl normalizes ../ before sending and the server receives /etc/passwd on the wrong route.
05 · URL Normalization Bypass
This is the most operationally important detail — and the reason naive reimplementations fail.
::5.1 The Problem
HTTP clients (Python requests, Go net/http, browsers) normalize paths before transmission:
The server gets /etc/passwd, which does not match /public/plugins/ → 404. Traversal is neutered on the client.
::5.2 Broken Approach
::5.3 Correct Approach (PreparedRequest Override)
Overwriting prepared.url after prepare() keeps the raw path in the HTTP request line. urllib3 does not re-normalize it.
06 · Post-Exploitation & High-Value Targets
Once directory traversal is confirmed, the immediate objective is identifying and exfiltrating files with maximum intelligence value. The following targets are prioritized in order of impact during a penetration test or red team engagement.
::6.1 /etc/passwd
Maps user accounts, UIDs, GIDs, home directories, and shell assignments. While password hashes are in /etc/shadow on modern systems (typically not readable without root), /etc/passwd provides the roadmap for lateral movement: identifying service accounts, administrative users, and the Grafana process owner.
::6.2 /etc/grafana/grafana.ini

The main Grafana configuration file. High-priority fields:
Extracting grafana.ini frequently provides a direct path to downstream infrastructure: the databases Grafana queries, the SMTP relay it uses for alerts, and the LDAP directory it authenticates against.
::6.3 /etc/grafana/ldap.toml
If LDAP integration is enabled, this file contains the bind DN and password used for directory queries:
A valid LDAP bind credential may allow enumeration or authentication against Active Directory or other directory services.
::6.4 /var/lib/grafana/grafana.db
The most consequential target. Grafana's internal SQLite database contains:
- ▹User accounts with PBKDF2-SHA256 password hashes (10,000 iterations)
- ▹Data source configurations with embedded plaintext credentials for Prometheus, InfluxDB, Elasticsearch, CloudWatch, and other backends
- ▹API keys and service account tokens
- ▹Dashboard and alert definitions (operational intelligence about monitored infrastructure)
- ▹Active session tokens
Because grafana.db is a binary SQLite file, it must be downloaded in raw byte mode. Any attempt to decode it as UTF-8 text will corrupt the binary structure and render it unreadable by SQLite.
::6.5 /proc/self/environ
On Linux, reading the process environment from /proc/self/environ reveals environment variables set in the Grafana process, which frequently include injected secrets:
Container deployments on Kubernetes and Docker are particularly likely to expose secrets this way, since environment variable injection is the standard pattern for passing secrets to containerized applications.
::6.6 /proc/self/cmdline and /proc/self/cwd
/proc/self/cmdline reveals the full command line used to launch Grafana, including any flags that expose configuration paths. /proc/self/cwd reveals the current working directory of the process. Together they help map the exact filesystem layout when the default paths are not accurate.
::6.7 Credential Processing with grafana2hashcat
After exfiltrating grafana.db, PBKDF2 hashes can be extracted and formatted for Hashcat:
Hashcat mode 10900 handles PBKDF2-HMAC-SHA256. The grafana2hashcat utility parses the SQLite user table and outputs lines in the format:
Successfully cracked hashes yield administrative access to the Grafana interface and, by extension, every data source it orchestrates.
07 · Automation with GrafTraverse
Manual exploitation does not scale across hundreds of endpoints. GrafTraverse is a command-line framework for authorized security testing of CVE-2021-43798. It fixes common request-handling bugs in public PoCs and adds multi-threaded scanning, plugin discovery, credential extraction, and structured reporting.
Repository: https://github.com/Asbawy/GrafTraverse-CVE-2021-43798

::7.1 Features
| Feature | Description |
|---|---|
| Multi-plugin brute-force | Probes 40+ built-in plugins to find a working traversal path |
| Interactive mode | Menu-driven single-target exploitation with live file preview |
| Batch mode | Concurrent multi-target scanning with configurable workers |
| Wordlist mode | Deep file enumeration from custom wordlists |
| Hash extraction | Auto-extracts PBKDF2 hashes from downloaded grafana.db |
| JSON / CSV logging | Structured output for reporting pipelines |
| Proxy & custom headers | Burp/ZAP integration and WAF-bypass headers |
| False-positive filtering | Rejects plugin JS/CSS/HTML responses |
::7.2 Installation
::7.3 Interactive Single Target
Menu options include /etc/passwd, grafana.ini, grafana.db, custom paths, full automated loot, and plugin scanning.
::7.4 Plugin Discovery Scan
::7.5 Batch Scanning
::7.6 Example Automation Workflow
08 · Remediation
::8.1 Primary Fix: Upgrade
| Branch | Minimum Patched |
|---|---|
| 8.3.x | 8.3.1 |
| 8.2.x | 8.2.7 |
| 8.1.x | 8.1.8 |
| 8.0.x | 8.0.7 |
| 7.5.x | 7.5.12 |
09 · Conclusion
CVE-2021-43798 demonstrates how a missing path confinement check on a public static-asset route becomes full filesystem read access. The traversal itself is simple; the hard part is building reliable automation that preserves malicious paths through HTTP client normalization.
For offensive practitioners, internalize the PreparedRequest override before writing any traversal tooling. For defenders, treat observability platforms as credential vaults — patch aggressively, segment network access, and monitor plugin asset routes.
GrafTraverse automates the repeatable parts of this assessment for authorized engagements: https://github.com/Asbawy/GrafTraverse-CVE-2021-43798
::References

Red Team Consultant · Penetration Tester · Bug Bounty Hunter
Offensive security professional with 250+ vulnerabilities reported across 50+ organizations including Atlassian, Vimeo, and AT&T. Sharing research, tools, and field notes.