Soccer
Executive summary — Soccer chains factory credentials on Tiny File Manager with an unrestricted file upload to get RCE as
www-data, discovers a second vhost from there exposing a Node application over WebSocket, exploits a blind, numeric-context SQL injection carried over that WebSocket (not HTTP, so standard tooling never reaches it) to steal database credentials, and closes the loop with a password reuse into SSH plus adoasrule that allows loading arbitrary Python plugins intodstatas root.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.43.221 |
Attack map
[80] soccer.htb — static site [9091] Express unidentified by nmap
│ directory brute force -> /tiny/ -> Tiny File Manager 2.4.3
▼
[tiny] admin:admin@123 (factory credentials) -> full file manager
│ unrestricted-extension upload to /tiny/uploads/ (served with PHP)
▼
[RCE] www-data (PHP webshell)
│ /etc/nginx/sites-enabled reveals hidden vhost soc-player.soccer.htb
│ -> proxies to Node on :3000, real WebSocket on :9091
▼
[9091] WebSocket — "id" parameter interpolated unquoted in a template literal
│ Blind SQL injection (numeric context) -> dump of accounts
▼
[creds] player:PlayerOftheMatch2022 (DB, plaintext) -> reused over SSH
▼
[SSH] player (user.txt)
│ doas NOPASSWD /usr/bin/dstat + plugin directory writable by
│ the player group -> dstat loads and runs Python as root
▼
[ROOT] malicious dstat Python plugin -> SUID bash -> root.txt
1. Reconnaissance
nmap -p- --min-rate 5000 -T4 -Pn -oN nmap-allports.txt 10.129.43.221
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9091/tcp open xmltec-xmlmail
9287/tcp filtered cumulus
22177/tcp filtered unknown
... (9 more "filtered" ports)
The scattered, randomly-numbered "filtered" ports are noise from the aggressive --min-rate (dropped SYNs with no response or RST), not a real firewall — a real firewall filters coherent ranges, not random stray ports. What matters is 9091/tcp open: a non-standard high port is usually a custom application, not a packaged, hardened service.
nmap -sCV -p22,80,9091 -Pn -oN nmap-services.txt 10.129.43.221
22/tcp open http OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://soccer.htb/
9091/tcp open xmltec-xmlmail?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 404 Not Found
| Content-Security-Policy: default-src 'none'
| X-Content-Type-Options: nosniff
| <pre>Cannot GET /</pre>
1 service unrecognized despite returning data.
nmap can't identify the service on 9091, but the fingerprint gives it away: Cannot GET / is the exact text of Express.js's default 404 handler, and the Content-Security-Policy/X-Content-Type-Options headers are the ones Express adds automatically to its error responses. It's a Node/Express app that serves nothing at / — either it only handles specific routes, or it doesn't speak plain HTTP at all.
echo "10.129.43.221 soccer.htb" | sudo tee -a /etc/hosts
With both surfaces confirmed (a static site on 80, an unidentified Express app on 9091), it's time to enumerate each.
Resumen ejecutivo — Soccer encadena unas credenciales de fábrica en Tiny File Manager con una subida de fichero sin restricción para conseguir RCE como
www-data, descubre desde ahí un segundo vhost que expone una aplicación Node por WebSocket, explota una SQL injection ciega en contexto numérico transportada sobre ese WebSocket (no HTTP, así que las herramientas estándar no llegan) para robar credenciales de la base de datos, y cierra el ciclo con una reutilización de contraseña hacia SSH y undoasque permite cargar plugins Python arbitrarios endstatcomo root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.43.221 |
Mapa del ataque
[80] soccer.htb — sitio estático [9091] Express sin identificar por nmap
│ gobuster de directorios -> /tiny/ -> Tiny File Manager 2.4.3
▼
[tiny] admin:admin@123 (credenciales de fábrica) -> gestor de ficheros completo
│ subida sin restricción de extensión a /tiny/uploads/ (servido con PHP)
▼
[RCE] www-data (webshell PHP)
│ /etc/nginx/sites-enabled revela vhost oculto soc-player.soccer.htb
│ -> proxy a Node en :3000, WebSocket real en :9091
▼
[9091] WebSocket — parámetro "id" interpolado sin comillas en un template literal
│ SQL injection ciega (contexto numérico) -> volcado de accounts
▼
[creds] player:PlayerOftheMatch2022 (BD, texto claro) -> reutilizada en SSH
▼
[SSH] player (user.txt)
│ doas NOPASSWD /usr/bin/dstat + directorio de plugins escribible
│ por el grupo player -> dstat carga y ejecuta Python como root
▼
[ROOT] plugin Python malicioso en dstat -> bash SUID -> root.txt
1. Reconocimiento
nmap -p- --min-rate 5000 -T4 -Pn -oN nmap-allports.txt 10.129.43.221
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9091/tcp open xmltec-xmlmail
9287/tcp filtered cumulus
22177/tcp filtered unknown
... (9 puertos "filtered" más)
Los puertos "filtered" dispersos y con números aleatorios son ruido del --min-rate agresivo (SYN perdidos sin respuesta ni RST), no un firewall real — un firewall filtra rangos coherentes, no puertos sueltos al azar. Lo relevante es 9091/tcp abierto: un puerto alto no estándar suele ser una aplicación propia, no un servicio empaquetado.
nmap -sCV -p22,80,9091 -Pn -oN nmap-services.txt 10.129.43.221
22/tcp open http OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://soccer.htb/
9091/tcp open xmltec-xmlmail?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 404 Not Found
| Content-Security-Policy: default-src 'none'
| X-Content-Type-Options: nosniff
| <pre>Cannot GET /</pre>
1 service unrecognized despite returning data.
Nmap no identifica el servicio del 9091, pero la huella lo delata: Cannot GET / es el texto exacto del manejador 404 por defecto de Express.js, y las cabeceras Content-Security-Policy/X-Content-Type-Options son las que Express añade automáticamente a sus errores. Es una aplicación Node/Express que no sirve nada en / — o solo atiende rutas concretas, o no habla HTTP plano en absoluto.
echo "10.129.43.221 soccer.htb" | sudo tee -a /etc/hosts
Con las dos superficies confirmadas (sitio estático en 80, aplicación Express sin identificar del todo en 9091), toca enumerar cada una.
This content is Root Access only. Everything else on the site — the free tier, the whole public library — stays open.
See Root Access plansNo account? Create one free then upgrade from your console.
Este contenido es solo para Root Access. Todo lo demás del sitio — el nivel gratuito, toda la biblioteca pública — sigue abierto.
Ver planes de Root Access¿Sin cuenta? Crea una gratis y luego mejora desde tu consola.