Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quality of life improvements #1439

Merged
merged 10 commits into from
May 3, 2024
76 changes: 43 additions & 33 deletions examples/secretsdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
17 changes: 12 additions & 5 deletions examples/smbclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]@]<targetName or address>')
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')
Expand Down Expand Up @@ -101,18 +102,24 @@ 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)
else:
print(line, end=' ')
else:
shell.cmdloop()

except Exception as e:
if logging.getLogger().level == logging.DEBUG:
import traceback
Expand Down
15 changes: 13 additions & 2 deletions impacket/examples/ntlmrelayx/attacks/httpattacks/adcsattack.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import re
import base64
import os
from OpenSSL import crypto

from impacket import LOG
Expand Down Expand Up @@ -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()
Expand All @@ -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))
Expand Down
5 changes: 3 additions & 2 deletions impacket/examples/ntlmrelayx/servers/httprelayserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -543,4 +544,4 @@ def run(self):
except KeyboardInterrupt:
pass
LOG.info('Shutting down HTTP Server')
self.server.server_close()
self.server.server_close()
3 changes: 2 additions & 1 deletion impacket/examples/ntlmrelayx/servers/smbrelayserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
15 changes: 13 additions & 2 deletions impacket/examples/secretsdump.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
39 changes: 37 additions & 2 deletions impacket/examples/smbclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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 = "./"
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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()

Expand Down
Loading