class Commands(API): def __init__(self, server): API.__init__(self) self.commands = self.get_api_functions() self.server = server self.available_modules = self.get_all_modules_paths() self.modules_handler = ModulesHandler(self) self.logger = logging.getLogger() self.options_parser = OptionsParser() self.port_scanner = PortScannerMT.Scanner(4000, 5000) self.report_generator = ReportGenerator() self.service_messages_handler = ServiceMessagesHandler() def get_api_functions(self): """ Find all api_wrapped methods in class Commands Returns (dict): method name => method """ api_methods = { k: v for k, v in vars(self.__class__).items() if inspect.isfunction(v) and v.__name__ == 'api_wrapped' } return api_methods def get_all_modules_paths(self): """Get common modules and modules from packs if available""" exploits = Modules.get_modules_names_dict(EXPLOITS_PATH) if not os.path.exists(PACKS_PATH): os.makedirs(PACKS_PATH) files = os.listdir(PACKS_PATH) for f in files: path_to_pack = os.path.join(PACKS_PATH, f) if os.path.isdir(path_to_pack): pack_dirs = [ fname.lower() for fname in os.listdir(path_to_pack) ] if "exploits" in pack_dirs: full_path_to_pack_exploits = os.path.join( path_to_pack, 'exploits') exploits.update( Modules.get_modules_names_dict( full_path_to_pack_exploits)) return exploits def _get_wrapped_function_required_args(self, func): if not hasattr(func, '__wrapped__'): return None args_spec = inspect.getargspec(func.__wrapped__) # Now slice 2 first arguments(self, client) and kw_args required_args = args_spec.args[2:-len(args_spec.defaults or [])] return args_spec.args, required_args def execute(self, message, client): """ Execution of command from websocket-client @param message:(Dict) Object, containing keys "command" and "args" @param client:(WebSocketHandler) Websocket client handler. Used to send response from server to this client """ if not message or type( message ) is not dict or "command" not in message or "args" not in message: self.send_error(client, 'Error while handling request') return command = message["command"] args = message["args"] uuid = message.get('uuid') args = args if args else {} if command not in self.commands: self.send_error(client, 'Wrong command') return func = self.commands[command] func_args, func_req_args = self._get_wrapped_function_required_args( func) # find missing or excess args func_args_set = set(func_args) func_req_args_set = set(func_req_args) input_args_set = set(args) intersection = func_req_args_set.intersection(input_args_set) # missing if len(intersection) != len(func_req_args_set): diff = func_req_args_set.difference(input_args_set) msg = 'Following required parameters are missing: %s' % ', '.join( diff) print(command, 'Error: %s' % msg) self.send_error(client, msg) return diff = input_args_set.difference(func_args_set) if diff: msg = 'Following parameters are excess: %s' % ', '.join(diff) print(command, 'Error: %s' % msg) self.send_error(client, msg) return # if no errors call func resp = func(self, client, **args) if uuid: client.send_message( dict(command='on_callback', args=resp, uuid=uuid)) @API.callable def start_module(self, client, module_name, use_listener, use_custom_port=False, custom_port=0, listener_type=1, options={}): """ Runs a module Args: module_name: (string) Name of module use_listener: (bool) If True - enable listener for module use_custom_port: (bool) Use custom listener port custom_port: (int) Custom listener port listener_type: (int) 1 - reverse, 2 - bind options: (dict) Option of module set up in GUI Returns: (dict): 'module_name': (string) Unique name of running module 'listener': (bool) Is listener enabled """ if module_name not in self.available_modules: print('There is no module with name %s' % module_name) return module_path = self.available_modules[module_name] new_module_name = self.modules_handler.make_unique_name(module_name) options = self.options_parser.parse_data(options) running_module = self.modules_handler.register_process( new_module_name, module_name, options) if use_listener and listener_type == 1: exclude_ports = self.modules_handler.get_busy_ports_list() if use_custom_port and custom_port: if custom_port in exclude_ports or self.port_scanner.check_port_state( custom_port): message = 'Lister port %d is busy. Try another port for listener' % custom_port return self.make_error(message) listener_options = dict(PORT=custom_port) else: free_socket_data = self.port_scanner.scan( search_for='closed', first_match=True, nthreads=10, exclude=exclude_ports) listener_options = dict(PORT=free_socket_data[0][1]) running_module.listener_options = listener_options listener_process = subprocess.Popen( [sys.executable, LISTENER_PATH, new_module_name], shell=False, env=os.environ.copy()) self.modules_handler.add_listener_pid(new_module_name, listener_process.pid) process = subprocess.Popen( [sys.executable, module_path, new_module_name], shell=False, env=os.environ.copy()) self.modules_handler.add_module_pid(new_module_name, process.pid) return dict(module_name=new_module_name, listener=use_listener) @API.callable def install_via_pip(self, client, library_name): """ Install python module via pip Args: library_name: Name of module to install """ import subprocess try: proc = subprocess.Popen(['pip', 'install', library_name]) except Exception as e: print e return self.make_error('Can\'t install module %s' % library_name) else: proc.communicate() if proc.returncode == 0: self.service_messages_handler.remove_import_error(library_name) return dict(module_to_import=library_name) return self.make_error('Can\'t install module %s' % library_name) @API.callable def get_all_server_data(self, client): """ Returns dict of modules, version, service messages """ data = [] self.service_messages_handler.reset() for name in self.available_modules: data.append([self.available_modules[name], name]) available_modules = self.modules_handler.get_modules_info(data) service_messages = self.service_messages_handler.get_grouped() # Get framework version module = self.modules_handler.import_from_uri("start.py", False) version = "?" if module and hasattr(module, "VERSION"): version = module.VERSION return dict(modules=available_modules, version=version, serviceMessages=service_messages) @API.callable def get_modules_log(self, client): """ Get all modules and listeners log """ modules = self.modules_handler.get_full_log() return modules @API.callable def kill_process(self, client, module_name): """ Kills running processes of module and listener if exists Args: module_name: (string) Name of module """ if module_name not in self.modules_handler.running_modules: return self.modules_handler.kill_process(module_name) @API.callable def register_module_message(self, client, message, state, module_name, type='text', inline=False, replace=False): """ Add log message from module Args: message: (string) Message from module state: (bool or None) State of module(success, fail or nothing) module_name: (string) Name og running module type: (string) text or image inline: (bool) Write on last line if True replace: (bool) Replace last line if True """ module = self.modules_handler.add_message(module_name, message, state, inline, replace, type) message = { "command": "on_module_message", "args": { "module_name": module.module_name, "message": module.log[-1].formatted(), "state": state } } # TODO REPORTS # if state is not None: # self.generate_report(pid) self.send_message_to_ui(message) return dict(message="ok") @API.callable def get_module_options(self, client, module_name): """ Send options of module to gui Args: module_name: real module name without '.py' extension Returns: (list) List of options from module's dict OPTIONS """ if module_name in self.available_modules: opts = self.modules_handler.get_available_options_for_module( self.available_modules[module_name]) opts = self.options_parser.prepare_options(opts) json_resp = [] for key in opts: json_resp.append(dict(option=key, value=opts[key])) return json_resp @API.callable def get_module_args(self, client, module_name): """ Get module options changed by GUI Args: module_name: (string) Name of running module Returns: (dict) Dict of options """ resp = self.modules_handler.get_changed_options(module_name) return resp @API.callable def gui_command_to_listener(self, client, module_name, message): """ Sends command from GUI to listener Args: module_name: (string) Name of running module message: (string) Message for listener from gui(os command) """ self.modules_handler.add_listener_message(module_name, ">> " + message) args = dict(module_name=module_name, message=message) self.send_message_to_listener(module_name, args) @API.callable def on_listener_message(self, client, module_name, message, state): """ Add message from listener to gui or get last command from gui to listener Args: module_name: (string) Name of running module message: (string) Message from listener state: (int) 0 - shell is not connected 1 - shell connected 2 - shell disconnected """ self.modules_handler.add_listener_message(module_name, message, state) data = dict(command="on_listener_message", args=dict(module_name=module_name, state=state, message=message)) self.send_message_to_ui(data) @API.callable def get_listener_options(self, client, module_name): """ Get listener options by listener PID or module PID Args: module_name: (string) Name of running module """ if not module_name: return self.make_error('PIDs are not specified') options = self.modules_handler.get_module_inst_by_name( module_name).listener_options return options @API.callable def add_listener_options(self, client, module_name, options): """ Adds/Changes options of listener Args: module_name: (string) Name of running module options: (dict) listener options """ module = self.modules_handler.get_module_inst_by_name(module_name) module.listener_options = options return {"re"} @API.callable def add_listener_pid(self, client, module_name, pid): """ Adds listener PID to running module instance Args: module_name: (string) Name of running module pid: (int) Listener PID """ self.modules_handler.add_listener_pid(module_name, pid) @API.callable def get_source(self, client, module_name): """ Get source code of module Args: module_name: (string) real module name, without '.py' extension """ with open(self.available_modules[module_name]) as f: lines = f.read().splitlines() source = "\n".join(lines) return dict(message=source, module_name=module_name) @API.callable def save_source(self, client, module_name, message): """ Save edited source code of module Args: module_name: (string) real module name, without '.py' extension message: (string) Edited source code of module """ host, port = client.socket.getsockname() if "localhost" not in host and "127.0.0.1" not in host: message = "Only localhost user can save sources" self.send_error(client, message) return code = message.encode('utf-8') with open(self.available_modules[module_name], 'wb') as f: f.write(code) self.send_info(client, 'Module %s successfully changed' % module_name) @API.callable def is_listener_connected(self, client, module_name): """ Get info about state of listener Args: module_name: (string) Name of running module """ state = None module = self.modules_handler.get_module_inst_by_name(module_name) if module: state = module.is_shell_connected if state == 0: state = False elif state == 1: state = True resp = dict(state=state) return resp def make_error(self, error_msg): return dict(error=True, message=error_msg) def send_error(self, client, error_msg): client.send_message( dict(command='on_error', args=dict(message=error_msg))) def send_info(self, client, info_msg): client.send_message( dict(command='on_info', args=dict(message=info_msg))) def generate_report(self, module_name): module_inst = self.modules_handler.get_module_inst_by_name(module_name) info = self.modules_handler.get_module_info( (self.available_modules[module_inst.original_name], module_name)) module_vars = { "LOG": module_inst.log, "RESULT": module_inst.state, "OPTIONS": module_inst.options } listener_vars = { "IS_SHELL_CONNECTED": module_inst.is_shell_connected, "LISTENER_OPTIONS": module_inst.listener_options, "LISTENER_LOG": '\n'.join(module_inst.listener_messages) } module_vars.update(info) module_vars.update(listener_vars) module_vars["CVE"] = module_vars["CVE Name"] self.report_generator.append_module(module_vars) def send_message_to_ui(self, message): self.server.send_message_to_all_uis(message) def send_message_to_listener(self, module_name, message): self.server.send_message_to_listener(module_name, message)