zaakir.io | blog

My thoughts

Hijacking Spring Boot Sessions via /actuator

Contents

  1. Preface
  2. Walkthrough

Preface

CozyHosting is fundamentally a lesson in Spring Boot Actuator misconfiguration. The /actuator/sessions endpoint — exposed by default in older Spring Boot versions if developers don't explicitly lock it down — leaks live JSESSIONID values keyed to authenticated usernames. An unauthenticated attacker can paste a leaked cookie into their browser and step straight into someone else's session, no credentials required. The takeaway: when you fingerprint a Java app, always enumerate the actuator namespace (/actuator, /actuator/env, /actuator/heapdump, /actuator/sessions) — these endpoints regularly leak the entire kingdom.

The box also reinforces the GTFObins reflex: sudo -l first, then look up whatever you find. Here it's sudo ssh, which has a known ProxyCommand abuse pattern that drops a root shell.

Walkthrough

Once connected to the box I started by running an nmap scan to identify any active services on the machine.

nmap -sC -sV -oA cozyhosting-nmap 10.10.11.224

-sC : default scripts -sV : enumerate versions -oA : output all formats

Initial Foothold

We are met with a login page.

Inspecting the page source I learn it uses a bootstrap template.

Searching the version and template name reveals the app is built on the Spring Boot framework. At this point I switched to directory enumeration with a Spring Boot–specific wordlist — SecLists has a robust one.

gobuster dir -w /usr/share/wordlists/seclists/Discovery/Web-Content/spring-boot.txt -u http://cozyhosting.htb

Several actuator endpoints are now visible. The interesting one is /actuator/sessions — a curl against it returns active session tokens mapped to usernames:

curl -X GET http://cozyhosting.htb/actuator/sessions | jq

Replacing the browser's JSESSIONID cookie with the leaked token drops me straight into the admin panel — no login required.

In the admin panel there's a "connection" section that takes hostname-style input. Bad input returns an SSH syntax error, which is a strong hint at command injection via the shell call. With revshells.com I crafted a payload that escapes the SSH command and connects back to my listener.

nc -lvnp 4444

Initial shell obtained. First habit: upgrade it.

python3 -c 'import pty; pty.spawn("/bin/bash")'

Listing the directory reveals a .jar executable. Standing up a Python server lets me exfiltrate it for inspection.

python3 -m http.server 8083

Decompiling the jar surfaces a few .properties files containing PostgreSQL credentials. Logging into psql, I dig through tables and find a (username, hash) pair. The hash is bcrypt — I throw it at John with rockyou.txt.

User flag obtained ✅

Privilege Escalation

Standard first check:

sudo -l

The user has sudo rights on ssh. GTFObins for ssh gives the payload directly:

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

Root shell. ⛳

HTB, Spring Boot, Web

Next post ➡
From PHP Upload Bypass to KeePass Cracking