diff --git a/examples/secretsdump.py b/examples/secretsdump.py index a881a8cef7..94f7db72c1 100755 --- a/examples/secretsdump.py +++ b/examples/secretsdump.py @@ -96,6 +96,8 @@ def __init__(self, remoteName, username='', password='', domain='', options=None self.__securityHive = options.security self.__samHive = options.sam self.__ntdsFile = options.ntds + self.__skipSam = options.skip_sam + self.__skipSecurity = options.skip_security self.__history = options.history self.__noLMHash = True self.__isRemote = True @@ -105,6 +107,7 @@ def __init__(self, remoteName, username='', password='', domain='', options=None self.__justDCNTLM = options.just_dc_ntlm self.__justUser = options.just_dc_user self.__ldapFilter = options.ldapfilter + self.__skipUser = options.skip_user self.__pwdLastSet = options.pwd_last_set self.__printUserStatus= options.user_status self.__resumeFileName = options.resumefile @@ -227,38 +230,40 @@ def dump(self): else: # If RemoteOperations succeeded, then we can extract SAM and LSA if self.__justDC is False and self.__justDCNTLM is False and self.__canProcessSAMLSA: - try: - if self.__isRemote is True: - SAMFileName = self.__remoteOps.saveSAM() - else: - SAMFileName = self.__samHive - - self.__SAMHashes = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote) - self.__SAMHashes.dump() - if self.__outputFileName is not None: - self.__SAMHashes.export(self.__outputFileName) - except Exception as e: - logging.error('SAM hashes extraction failed: %s' % str(e)) - - try: - if self.__isRemote is True: - SECURITYFileName = self.__remoteOps.saveSECURITY() - else: - SECURITYFileName = self.__securityHive - - self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, + if not self.__skipSam: + try: + if self.__isRemote is True: + SAMFileName = self.__remoteOps.saveSAM() + else: + SAMFileName = self.__samHive + + self.__SAMHashes = SAMHashes(SAMFileName, bootKey, isRemote = self.__isRemote) + self.__SAMHashes.dump() + if self.__outputFileName is not None: + self.__SAMHashes.export(self.__outputFileName) + except Exception as e: + logging.error('SAM hashes extraction failed: %s' % str(e)) + + if not self.__skipSecurity: + try: + if self.__isRemote is True: + SECURITYFileName = self.__remoteOps.saveSECURITY() + else: + SECURITYFileName = self.__securityHive + + self.__LSASecrets = LSASecrets(SECURITYFileName, bootKey, self.__remoteOps, isRemote=self.__isRemote, history=self.__history) - self.__LSASecrets.dumpCachedHashes() - if self.__outputFileName is not None: - self.__LSASecrets.exportCached(self.__outputFileName) - self.__LSASecrets.dumpSecrets() - if self.__outputFileName is not None: - self.__LSASecrets.exportSecrets(self.__outputFileName) - except Exception as e: - if logging.getLogger().level == logging.DEBUG: - import traceback - traceback.print_exc() - logging.error('LSA hashes extraction failed: %s' % str(e)) + self.__LSASecrets.dumpCachedHashes() + if self.__outputFileName is not None: + self.__LSASecrets.exportCached(self.__outputFileName) + self.__LSASecrets.dumpSecrets() + if self.__outputFileName is not None: + self.__LSASecrets.exportSecrets(self.__outputFileName) + except Exception as e: + if logging.getLogger().level == logging.DEBUG: + import traceback + traceback.print_exc() + logging.error('LSA hashes extraction failed: %s' % str(e)) # NTDS Extraction we can try regardless of RemoteOperations failing. It might still work if self.__isRemote is True: @@ -273,8 +278,9 @@ def dump(self): noLMHash=self.__noLMHash, remoteOps=self.__remoteOps, useVSSMethod=self.__useVSSMethod, justNTLM=self.__justDCNTLM, pwdLastSet=self.__pwdLastSet, resumeSession=self.__resumeFileName, - outputFileName=self.__outputFileName, justUser=self.__justUser, - ldapFilter=self.__ldapFilter, printUserStatus=self.__printUserStatus) + outputFileName=self.__outputFileName, justUser=self.__justUser, + skipUser=self.__skipUser, ldapFilter=self.__ldapFilter, + printUserStatus=self.__printUserStatus) try: self.__NTDSHashes.dump() except Exception as e: @@ -360,6 +366,8 @@ def cleanup(self): parser.add_argument('-resumefile', action='store', help='resume file name to resume NTDS.DIT session dump (only ' 'available to DRSUAPI approach). This file will also be used to keep updating the session\'s ' 'state') + parser.add_argument('-skip-sam', action='store_true', help='Do NOT parse the SAM hive on remote system') + parser.add_argument('-skip-security', action='store_true', help='Do NOT parse the SECURITY hive on remote system') parser.add_argument('-outputfile', action='store', help='base output filename. Extensions will be added for sam, secrets, cached and ntds') parser.add_argument('-use-vss', action='store_true', default=False, @@ -382,6 +390,8 @@ def cleanup(self): help='Extract only NTDS.DIT data (NTLM hashes and Kerberos keys)') group.add_argument('-just-dc-ntlm', action='store_true', default=False, help='Extract only NTDS.DIT data (NTLM hashes only)') + group.add_argument('-skip-user', action='store', help='Do NOT extract NTDS.DIT data for the user specified. ' + 'Can provide comma-separated list of users to skip, or text file with one user per line') group.add_argument('-pwd-last-set', action='store_true', default=False, help='Shows pwdLastSet attribute for each NTDS.DIT account. Doesn\'t apply to -outputfile data') group.add_argument('-user-status', action='store_true', default=False, diff --git a/examples/smbclient.py b/examples/smbclient.py index 7605268342..0d27711f07 100755 --- a/examples/smbclient.py +++ b/examples/smbclient.py @@ -35,7 +35,8 @@ def main(): parser = argparse.ArgumentParser(add_help = True, description = "SMB client implementation.") parser.add_argument('target', action='store', help='[[domain/]username[:password]@]') - parser.add_argument('-file', type=argparse.FileType('r'), help='input file with commands to execute in the mini shell') + parser.add_argument('-inputfile', type=argparse.FileType('r'), help='input file with commands to execute in the mini shell') + parser.add_argument('-outputfile', action='store', help='Output file to log smbclient actions in') parser.add_argument('-debug', action='store_true', help='Turn DEBUG output ON') group = parser.add_argument_group('authentication') @@ -101,11 +102,16 @@ def main(): else: smbClient.login(username, password, domain, lmhash, nthash) - shell = MiniImpacketShell(smbClient) + shell = MiniImpacketShell(smbClient, None, options.outputfile) - if options.file is not None: - logging.info("Executing commands from %s" % options.file.name) - for line in options.file.readlines(): + if options.outputfile is not None: + f = open(options.outputfile, 'a') + f.write('=' * 20 + '\n' + options.target_ip + '\n' + '=' * 20 + '\n') + f.close() + + if options.inputfile is not None: + logging.info("Executing commands from %s" % options.inputfile.name) + for line in options.inputfile.readlines(): if line[0] != '#': print("# %s" % line, end=' ') shell.onecmd(line) @@ -113,6 +119,7 @@ def main(): print(line, end=' ') else: shell.cmdloop() + except Exception as e: if logging.getLogger().level == logging.DEBUG: import traceback diff --git a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py index 8481f2e092..3a94c76f85 100644 --- a/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py +++ b/impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py @@ -15,6 +15,7 @@ import re import base64 +import os from OpenSSL import crypto from impacket import LOG @@ -58,7 +59,7 @@ def _run(self): response = self.client.getresponse() if response.status != 200: - LOG.error("Error getting certificate! Make sure you have entered valid certiface template.") + LOG.error("Error getting certificate! Make sure you have entered valid certificate template.") return content = response.read() @@ -76,7 +77,17 @@ def _run(self): certificate = response.read().decode() certificate_store = self.generate_pfx(key, certificate) - LOG.info("Base64 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + LOG.info("Writing certificate to %s/%s.pfx" % (self.config.lootdir, self.username)) + try: + if not os.path.isdir(self.config.lootdir): + os.mkdir(self.config.lootdir) + with open("%s/%s.pfx" % (self.config.lootdir, self.username), 'wb') as f: + f.write(certificate_store) + LOG.info("Certificate successfully written to file") + except Exception as e: + LOG.info("Unable to write certificate to file, printing B64 of certificate to console instead") + LOG.info("Base64 certificate of user %s: \n%s" % (self.username, base64.b64encode(certificate_store).decode())) + pass if self.config.altName: LOG.info("This certificate can also be used for user : {}".format(self.config.altName)) diff --git a/impacket/examples/ntlmrelayx/servers/httprelayserver.py b/impacket/examples/ntlmrelayx/servers/httprelayserver.py index 68db7d36a3..57030fc5ac 100644 --- a/impacket/examples/ntlmrelayx/servers/httprelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/httprelayserver.py @@ -469,7 +469,8 @@ def do_relay(self, messageType, token, proxy, content = None): writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'], self.server.config.outputFile) - self.server.config.target.logTarget(self.target, True, self.authUser) + if not self.server.config.isADCSAttack: + self.server.config.target.logTarget(self.target, True, self.authUser) self.do_attack() if self.server.config.disableMulti: # We won't use the redirect trick, closing connection... @@ -543,4 +544,4 @@ def run(self): except KeyboardInterrupt: pass LOG.info('Shutting down HTTP Server') - self.server.server_close() \ No newline at end of file + self.server.server_close() diff --git a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py index 9e02b2ea17..a61025c883 100644 --- a/impacket/examples/ntlmrelayx/servers/smbrelayserver.py +++ b/impacket/examples/ntlmrelayx/servers/smbrelayserver.py @@ -359,7 +359,8 @@ def SmbSessionSetup(self, connId, smbServer, recvPacket): # We have a session, create a thread and do whatever we want LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser)) # Log this target as processed for this client - self.targetprocessor.logTarget(self.target, True, self.authUser) + if not self.config.isADCSAttack: + self.targetprocessor.logTarget(self.target, True, self.authUser) ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'], authenticateMessage['user_name'], diff --git a/impacket/examples/secretsdump.py b/impacket/examples/secretsdump.py index 3c885d1d2d..68add5e868 100644 --- a/impacket/examples/secretsdump.py +++ b/impacket/examples/secretsdump.py @@ -1996,7 +1996,7 @@ class CRYPTED_BLOB(Structure): def __init__(self, ntdsFile, bootKey, isRemote=False, history=False, noLMHash=True, remoteOps=None, useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=None, - justUser=None, ldapFilter=None, printUserStatus=False, + justUser=None, skipUser=None,ldapFilter=None, printUserStatus=False, perSecretCallback = lambda secretType, secret : _print_helper(secret), resumeSessionMgr=ResumeSessionMgrInFile): self.__bootKey = bootKey @@ -2020,6 +2020,7 @@ def __init__(self, ntdsFile, bootKey, isRemote=False, history=False, noLMHash=Tr self.__outputFileName = outputFileName self.__justUser = justUser self.__ldapFilter = ldapFilter + self.__skipUser = skipUser self.__perSecretCallback = perSecretCallback # these are all the columns that we need to get the secrets. @@ -2499,7 +2500,16 @@ def dump(self): hashesOutputFile = None keysOutputFile = None clearTextOutputFile = None + skipUsers = [] + if self.__skipUser: + if os.path.isfile(self.__skipUser): + f = open(self.__skipUser, 'r') + skipUsers = [ line.strip() for line in f ] + f.close() + else: + skipUsers = self.__skipUser.split(',') + if self.__useVSSMethod is True: if self.__NTDS is None: # No NTDS.dit file provided and were asked to use VSS @@ -2702,7 +2712,8 @@ def dump(self): for user in resp['Buffer']['Buffer']: userName = user['Name'] - + if userName in skipUsers: + continue userSid = "%s-%i" % (self.__remoteOps.getDomainSid(), user['RelativeId']) if resumeSid is not None: # Means we're looking for a SID before start processing back again diff --git a/impacket/examples/smbclient.py b/impacket/examples/smbclient.py index 81a9c01fd6..4bd4a640f8 100755 --- a/impacket/examples/smbclient.py +++ b/impacket/examples/smbclient.py @@ -42,7 +42,7 @@ import readline class MiniImpacketShell(cmd.Cmd): - def __init__(self, smbClient, tcpShell=None): + def __init__(self, smbClient, tcpShell=None, outputfile=None): #If the tcpShell parameter is passed (used in ntlmrelayx), # all input and output is redirected to a tcp socket # instead of to stdin / stdout @@ -67,12 +67,17 @@ def __init__(self, smbClient, tcpShell=None): self.loggedIn = True self.last_output = None self.completion = [] + self.outputfile = outputfile def emptyline(self): pass def precmd(self,line): # switch to unicode + if self.outputfile is not None: + f = open(self.outputfile, 'a') + f.write('> ' + line + "\n") + f.close() if PY2: return line.decode('utf-8') return line @@ -325,8 +330,14 @@ def do_shares(self, line): LOG.error("Not logged in") return resp = self.smb.listShares() + if self.outputfile is not None: + f = open(self.outputfile, 'a') for i in range(len(resp)): + if self.outputfile: + f.write(resp[i]['shi1_netname'][:-1] + '\n') print(resp[i]['shi1_netname'][:-1]) + if self.outputfile: + f.close() def do_use(self,line): if self.loggedIn is False: @@ -372,6 +383,10 @@ def do_pwd(self,line): LOG.error("Not logged in") return print(self.pwd.replace("\\","/")) + if self.outputfile is not None: + f = open(self.outputfile, 'a') + f.write(self.pwd.replace("\\","/")) + f.close() def do_ls(self, wildcard, display = True): if self.loggedIn is False: @@ -387,12 +402,22 @@ def do_ls(self, wildcard, display = True): self.completion = [] pwd = pwd.replace('/','\\') pwd = ntpath.normpath(pwd) + if self.outputfile is not None: + of = open(self.outputfile, 'a') for f in self.smb.listPath(self.share, pwd): if display is True: + if self.outputfile: + of.write("%crw-rw-rw- %10d %s %s" % ( + 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())), + f.get_longname()) + "\n") + print("%crw-rw-rw- %10d %s %s" % ( 'd' if f.is_directory() > 0 else '-', f.get_filesize(), time.ctime(float(f.get_mtime_epoch())), f.get_longname())) self.completion.append((f.get_longname(), f.is_directory())) + if self.outputfile: + of.close() + def do_lls(self, currentDir): if currentDir == "": currentDir = "./" @@ -460,7 +485,6 @@ def do_tree(self, filepath): pass print("Finished - " + str(totalFilesRead) + " files and folders") - def do_rm(self, filename): if self.tid is None: LOG.error("No share selected") @@ -575,14 +599,25 @@ def do_cat(self, filename): output = fh.getvalue() encoding = chardet.detect(output)["encoding"] error_msg = "[-] Output cannot be correctly decoded, are you sure the text is readable ?" + if self.outputfile is not None: + f = open(self.outputfile, 'a') if encoding: try: + if self.outputfile: + f.write(output.decode(encoding) + '\n') + f.close() print(output.decode(encoding)) except: + if self.outputfile: + f.write(error_msg + '\n') + f.close() print(error_msg) finally: fh.close() else: + if self.outpufile: + f.write(error_msg + '\n') + f.close() print(error_msg) fh.close()