def open(self): mainDir = os.path.normpath(Utils.getMainDir()) mongoInstance = MongoCalendar.getInstance() with open(os.path.join(mainDir, "setupTerminalForPentest.sh"), "r") as f: data = f.read() lines = data.split("\n") lines[0] = "POLLENISATOR_CURRENT_DB="+str(mongoInstance.calendarName) data = "\n".join(lines) with open(os.path.join(mainDir, "setupTerminalForPentest.sh"), "w") as f: f.write(data) favorite = self.settings.getFavoriteTerm() if favorite is None: tkinter.messagebox.showerror("Terminal settings invalid", "None of the terminals given in the settings are installed on this computer.") return False if which(favorite) is not None: terms = self.settings.getTerms() terms_dict = {} for term in terms: terms_dict[term.split(" ")[0]] = term command_term = terms_dict.get(favorite, None) if command_term is not None: Utils.execute(terms_dict[favorite]) return True else: tkinter.messagebox.showerror("Terminal settings invalid", "Check your terminal settings") else: tkinter.messagebox.showerror("Terminal settings invalid", "The selected favorite terminal is not available on this computer.") return False
def addOnlineWorker(self, worker_hostname): """ Register a celery worker on the worker's list. Also deletes old queues and messages Args: worker_hostname: the worker hostname to register on worker's list """ mongoInstance = MongoCalendar.getInstance() agg_queues = mongoInstance.aggregateFromDb( "broker_pollenisator", "messages.routing", [{ "$group": { "_id": "$queue" } }, { "$match": { "_id": { "$regex": "^.*&" + worker_hostname + "&.*$" } } }]) mongoInstance.deleteFromDb( "broker_pollenisator", "messages.routing", {"queue": { "$regex": "^.*&" + worker_hostname + "&.*$" }}, True) for agg_queue in agg_queues: Utils.execute( "celery -A slave purge -f -Q '" + agg_queue["_id"] + "'", None, False) self.workerRegisterCommands(worker_hostname)
def getIpsFitting(self): """Returns a list of ip mongo dict fitting this scope Returns: A list ip IP dictionnary from mongo db """ mongoInstance = MongoCalendar.getInstance() ips = mongoInstance.find("ips", ) ips_fitting = [] isdomain = self.isDomain() for ip in ips: if isdomain: my_ip = Utils.performLookUp(self.scope) my_domain = self.scope ip_isdomain = not Utils.isIp(ip["ip"]) if ip_isdomain: if my_domain == ip["ip"]: ips_fitting.append(ip) if Scope.isSubDomain(my_domain, ip["ip"]): ips_fitting.append(ip) else: if my_ip == ip["ip"]: ips_fitting.append(ip) else: if Ip.checkIpScope(self.scope, ip["ip"]): ips_fitting.append(ip) return ips_fitting
def autoScan(databaseName, endless, useReprinter=False): """ Search tools to launch within defined conditions and attempts to launch them on celery workers. Gives a visual feedback on stdout Args: databaseName: The database to search tools in endless: a boolean that indicates if the autoscan will be endless or if it will stop at the moment it does not found anymore launchable tools. useReprinter: a boolean that indicates if the array outpur will be entirely reprinted or if it will be overwritten. """ mongoInstance = MongoCalendar.getInstance() mongoInstance.connectToDb(databaseName) my_monitor = Monitor(databaseName) Utils.resetUnfinishedTools() time_compatible_waves_id = Wave.searchForAddressCompatibleWithTime() killer = GracefulKiller() if not endless: killer.kill_now = len(time_compatible_waves_id) <= 0 print("No wave compatible") else: killer.kill_now = False if useReprinter: reprinter = Reprinter() else: reprinter = None max_tabulation = _getMaxColumnLen() while not killer.kill_now: # Extract commands with compatible time and not yet done launchableTools, waiting = findLaunchableTools() # Sort by command priority launchableTools.sort(key=lambda tup: int(tup["priority"])) dispatchLaunchableTools(my_monitor, launchableTools) printStatus(max_tabulation, waiting, reprinter) time.sleep(3) my_monitor.stop()
def resetUnfinishedTools(self): """ Reset all running tools to a ready state. """ mongoInstance = MongoCalendar.getInstance() if mongoInstance.hasACalendarOpen(): Utils.resetUnfinishedTools() self.treevw.load()
def doInsert(self, values): """ Insert the Scope represented by this model in the database with the given values. Args: values: A dictionary crafted by MultipleScopeView or ScopeView containg all form fields values needed. Returns: { '_id': The mongo ObjectId _id of the inserted command document. 'nbErrors': The number of objects that has not been inserted in database due to errors. } """ # Only multi insert exists at the moment for Scope # Get form values wave = values["wave"] ret = [] total = 0 accepted = 0 insert_setting = values["Settings"] split_range_setting = values.get("Split", False) for line in values["Scopes"].split("\n"): if line.strip() != "": # Insert in database scopeToAdd = line.strip() if Utils.isIp(scopeToAdd): scopeToAdd += "/32" if Utils.isNetworkIp(scopeToAdd): if split_range_setting: network_ips = Utils.splitRange(scopeToAdd) if len(network_ips) == 0: model = Scope().initialize(wave, scopeToAdd, "") inserted_res, iid = model.addInDb() else: for network_ip in network_ips: model = Scope().initialize(wave, str(network_ip), "") inserted_res, iid = model.addInDb() if inserted_res: accepted += 1 ret.append(iid) total += 1 else: model = Scope().initialize(wave, scopeToAdd, "") inserted_res, iid = model.addInDb() else: model = Scope().initialize(wave, scopeToAdd, "") inserted_res, iid = model.addDomainInDb(insert_setting) if inserted_res == 1: accepted += 1 ret.append(iid) total += 1 return ret, total-accepted # nb errors = total - accepted
def __init__(self, parent, settings): """ Constructor """ self.dashboardFrame = None self.parent = None self.treevw = None self.style = None self.ips = None self.ports = None self.tools = None iconPath = Utils.getIconDir() self.icons = {} self.icons["tool"] = ImageTk.PhotoImage( Image.open(iconPath + "tool.png")) self.icons["cross"] = ImageTk.PhotoImage( Image.open(iconPath + "cross.png")) self.icons["running"] = ImageTk.PhotoImage( Image.open(iconPath + "running.png")) self.icons["done"] = ImageTk.PhotoImage( Image.open(iconPath + "done_tool.png")) self.icons["ready"] = ImageTk.PhotoImage( Image.open(iconPath + "waiting.png")) self.icons["Not ready"] = ImageTk.PhotoImage( Image.open(iconPath + "cross.png"))
def addAllTool(self, command_name): """ Add the appropriate tools (level check and wave's commands check) for this scope. Args: command_name: The command that we want to create all the tools for. """ mongoInstance = MongoCalendar.getInstance() command = mongoInstance.findInDb("pollenisator", "commands", {"name": command_name}, False) if command["lvl"] == "network": newTool = Tool() newTool.initialize(command["name"], self.wave, self.scope, "", "", "", "network") newTool.addInDb() return if command["lvl"] == "domain": if not Utils.isNetworkIp(self.scope): newTool = Tool() newTool.initialize(command["name"], self.wave, self.scope, "", "", "", "domain") newTool.addInDb() return ips = self.getIpsFitting() for ip in ips: i = Ip(ip) i.addAllTool(command_name, self.wave, self.scope)
def setToolsInTime(self): """Get all OOT (Out of Time) tools in this wave and checks if this Interval makes them in time. If it is the case, set them in time. """ if Utils.fitNowTime(self.dated, self.datef): tools = Tool.fetchObjects({"wave": self.wave, "status": "OOT"}) for tool in tools: tool.setInTime()
def checkDomainFit(cls, waveName, domain): """ Check if a found domain belongs to one of the scope of the given wave. Args: waveName: The wave id (name) you want to search for a validating scope domain: The found domain. Returns: boolean """ # Checking settings for domain check. settings = Settings() # get the domain ip so we can search for it in ipv4 range scopes. domainIp = Utils.performLookUp(domain) mongoInstance = MongoCalendar.getInstance() scopesOfWave = mongoInstance.find("scopes", {"wave": waveName}) for scopeOfWave in scopesOfWave: scopeIsANetworkIp = Utils.isNetworkIp(scopeOfWave["scope"]) if scopeIsANetworkIp: if settings.db_settings.get("include_domains_with_ip_in_scope", False): if Ip.checkIpScope(scopeOfWave["scope"], domainIp): return True else: # If scope is domain # check if we include subdomains if settings.db_settings.get("include_all_domains", False): return True else: splitted_domain = domain.split(".") # Assuring to check only if there is a domain before the tld (.com, .fr ... ) topDomainExists = len(splitted_domain) > 2 if topDomainExists: if settings.db_settings.get( "include_domains_with_topdomain_in_scope", False): if splitted_domain[1:] == scopeOfWave[ "scope"].split("."): return True if settings.db_settings.get( "include_domains_with_ip_in_scope", False): inRangeDomainIp = Utils.performLookUp( scopeOfWave["scope"]) if str(inRangeDomainIp) == str(domainIp): return True return False
def connect(self, config=None, timeoutInMS=500): """ Connect the mongo client to the database using the login provided and ssl certificates if ssl is activated. Args: config: A dictionnary with client.cfg config values (host, mongo_port, password, user, ssl). Default to None. If None, the client.cfg file will be read. timeoutInMs: milliseconds to wait before timeout. Default to 500ms. Raises: ServerSelectionTimeoutError: if unable to connect to the mongo database OperationFailure: if unable to authenticate using user/password. Returns: None if not connected False if connection failed True if connected succeeded """ if self.client is not None: return dir_path = os.path.dirname(os.path.realpath(__file__)) cfg = config if config is not None else Utils.loadCfg( os.path.join(dir_path, "../../config/client.cfg")) try: self.host = str(cfg["host"]) self.port = str(cfg.get("mongo_port", 27017)) self.password = str(cfg["password"]) self.user = str(cfg["user"]) self.ssl = str(cfg["ssl"]) connectionString = "" if self.user != "": connectionString = self.user + ':' + self.password + '@' self.calendarName = None try: if cfg["ssl"] == "True": self.ssldir = os.path.abspath( os.path.join(dir_path, "../../ssl/")) self.client = MongoClient( 'mongodb://' + connectionString + self.host + ":" + self.port, ssl=True, ssl_certfile=os.path.join(self.ssldir, "client.pem"), ssl_cert_reqs=ssl.CERT_REQUIRED, ssl_ca_certs=os.path.join(self.ssldir, "ca.pem"), serverSelectionTimeoutMS=timeoutInMS, socketTimeoutMS=2000, connectTimeoutMS=2000) else: self.client = MongoClient( 'mongodb://' + connectionString + self.host + ":" + self.port, serverSelectionTimeoutMS=timeoutInMS) server_info = self.client.server_info() return True and self.client is not None and server_info is not None except ServerSelectionTimeoutError as e: # Unable to connect raise e except OperationFailure as e: # Authentication failed raise e except KeyError as e: raise e return False
def downloadResultFile(self, _event=None): """Callback for tool click #TODO move to ToolController Download the tool result file and asks the user if he or she wants to open it. If OK, tries to open it using xdg-open or os.startsfile Args: _event: not used """ fs = FileStorage() fs.open() path = None if fs.sftp_connection is not None: dialog = ChildDialogInfo(self.appliViewFrame, "Download Started", "Downloading...") resultFile = self.controller.getResultFile() dialog.show() if resultFile != "" and resultFile is not None: path = fs.getToolResult(resultFile) else: tkinter.messagebox.showerror( "Download failed", "The result file does not exist.") dialog.destroy() else: tkinter.messagebox.showerror("Download failed", "The sftp connection failed.") return fs.close() if path is not None: if os.path.isfile(path): if which("xdg-open") is not None: dialog = ChildDialogQuestion( self.appliViewFrame, "Download completed", "The file has been downloaded.\n Would you like to open it?", answers=["Open", "Cancel"]) self.appliViewFrame.wait_window(dialog.app) if dialog.rvalue == "Open": Utils.execute("xdg-open " + path) return else: return path = None if path is None: tkinter.messagebox.showerror( "Download failed", "the file does not exist on sftp server")
def prepareCalendar(self, dbName, pentest_type, start_date, end_date, scope, settings, pentesters): """ Initiate a pentest database with wizard info Args: dbName: the database name pentest_type: a pentest type choosen from settings pentest_types. Used to select commands that will be launched by default start_date: a begining date and time for the pentest end_date: ending date and time for the pentest scope: a list of scope valid string (IP, network IP or host name) settings: a dict of settings with keys: * "Add domains whose IP are in scope": if 1, will do a dns lookup on new domains and check if found IP is in scope * "Add domains who have a parent domain in scope": if 1, will add a new domain if a parent domain is in scope * "Add all domains found": Unsafe. if 1, all new domains found by tools will be considered in scope. """ commands = Command.getList({"$or":[{"types":{"$elemMatch":{"$eq":pentest_type}}}, {"types":{"$elemMatch":{"$eq":"Commun"}}}]}) if not commands: commandslist = Command.getList() if not commandslist: dialog = ChildDialogQuestion(self.parent, "No command found", "There is no registered command in the database. Would you like to import the default set?") self.parent.wait_window(dialog.app) if dialog.rvalue != "Yes": return default = os.path.join(Utils.getMainDir(), "exports/pollenisator_commands.gzip") res = self.importCommands(default) if res: default = os.path.join(Utils.getMainDir(), "exports/pollenisator_group_commands.gzip") res = self.importCommands(default) commands = Command.getList({"$or":[{"types":{"$elemMatch":{"$eq":pentest_type}}}, {"types":{"$elemMatch":{"$eq":"Commun"}}}]}) #Duplicate commands in local database allcommands = Command.fetchObjects({}) for command in allcommands: command.indb = MongoCalendar.getInstance().calendarName command.addInDb() Wave().initialize(dbName, commands).addInDb() Interval().initialize(dbName, start_date, end_date).addInDb() values = {"wave":dbName, "Scopes":scope, "Settings":False} ScopeController(Scope()).doInsert(values) self.settings.reloadSettings() self.settings.db_settings["pentest_type"] = pentest_type self.settings.db_settings["include_domains_with_ip_in_scope"] = settings['Add domains whose IP are in scope'] == 1 self.settings.db_settings["include_domains_with_topdomain_in_scope"] = settings["Add domains who have a parent domain in scope"] == 1 self.settings.db_settings["include_all_domains"] = settings["Add all domains found"] == 1 self.settings.db_settings["pentesters"] = list(map(lambda x: x.strip(), pentesters.split("\n"))) self.settings.save()
def initMainView(self): """ Fill the main view tab menu """ self.mainPageFrame = ttk.Frame(self.nbk) searchFrame = ttk.Frame(self.mainPageFrame) lblSearch = ttk.Label(searchFrame, text="Filter bar:") lblSearch.pack(side="left", fill=tk.NONE) self.searchBar = AutocompleteEntry(self.settings, searchFrame) #self.searchBar = ttk.Entry(searchFrame, width=108) self.searchBar.bind('<Return>', self.newSearch) self.searchBar.bind('<KP_Enter>', self.newSearch) self.searchBar.bind('<Control-a>', self.searchbarSelectAll) # searchBar.bind("<Button-3>", self.do_popup) self.searchBar.pack(side="left", fill="x", expand=True) btnSearchBar = ttk.Button(searchFrame, text="Search", command=self.newSearch) btnSearchBar.pack(side="left", fill="x") btnReset = ttk.Button(searchFrame, text="Reset",command=self.resetButtonClicked) btnReset.pack(side="left", fill="x") self.btnHelp = ttk.Button(searchFrame) self.photo = tk.PhotoImage(file=Utils.getHelpIconPath()) self.helpFrame = None self.btnHelp.config(image=self.photo, command=self.showSearchHelp) self.btnHelp.pack(side="left") searchFrame.pack(side="top", fill="x") #PANED PART self.paned = tk.PanedWindow(self.mainPageFrame, height=800) #RIGHT PANE : Canvas + frame self.canvasMain = tk.Canvas(self.paned, bg="white") self.viewframe = ttk.Frame(self.canvasMain) #LEFT PANE : Treeview self.frameTw = ttk.Frame(self.paned) self.treevw = CalendarTreeview(self, self.frameTw) self.treevw.initUI() scbVSel = ttk.Scrollbar(self.frameTw, orient=tk.VERTICAL, command=self.treevw.yview) self.treevw.configure(yscrollcommand=scbVSel.set) self.treevw.grid(row=0, column=0, sticky=tk.NSEW) scbVSel.grid(row=0, column=1, sticky=tk.NS) self.paned.add(self.frameTw) self.myscrollbarMain = tk.Scrollbar(self.paned, orient="vertical", command=self.canvasMain.yview) self.myscrollbarMain.pack(side="right", fill=tk.BOTH) self.canvasMain.bind('<Enter>', self.boundToMousewheelMain) self.canvasMain.bind('<Leave>', self.unboundToMousewheelMain) self.canvasMain.pack(side="left") self.canvasMain.bind('<Configure>', self.resizeCanvasMainFrame) self.canvas_main_frame = self.canvasMain.create_window((0, 0), window=self.viewframe, anchor='nw') self.viewframe.bind("<Configure>", self.scrollFrameMainFunc) self.canvasMain.configure(yscrollcommand=self.myscrollbarMain.set) self.paned.add(self.canvasMain) self.paned.pack(fill=tk.BOTH, expand=1) self.frameTw.rowconfigure(0, weight=1) # Weight 1 sur un layout grid, sans ça le composant ne changera pas de taille en cas de resize self.frameTw.columnconfigure(0, weight=1) # Weight 1 sur un layout grid, sans ça le composant ne changera pas de taille en cas de resize self.nbk.add(self.mainPageFrame, text="Main View ", image=self.main_tab_img, compound=tk.TOP, sticky='nsew')
def isLaunchableNow(self): """Returns True if the tool matches criteria to be launched (current time matches one of interval object assigned to this wave) Returns: bool """ intervals = Interval.fetchObjects({"wave": self.wave}) for intervalModel in intervals: if Utils.fitNowTime(intervalModel.dated, intervalModel.datef): return True return False
def getCommandToExecute(self, outputDirectory): """ Get the tool bash command to execute. Replace the command's text's variables with tool's informations. Args: outputDirectory: the output directory for this tool (see getOutputDir) Return: Returns the bash command of this tool instance. """ toolHasCommand = self.text if toolHasCommand is not None and toolHasCommand.strip() != "": command = self.text lvl = self.lvl else: comm = self.getCommand() command = comm["text"] lvl = comm["lvl"] mongoInstance = MongoCalendar.getInstance() command = command.replace("|outputDir|", outputDirectory) command = command.replace("|wave|", self.wave) if lvl == "network" or lvl == "domain": command = command.replace("|scope|", self.scope) if Utils.isNetworkIp(self.scope) == False: depths = self.scope.split(".") if len(depths) > 2: topdomain = ".".join(depths[1:]) else: topdomain = ".".join(depths) command = command.replace("|parent_domain|", topdomain) if lvl == "ip": command = command.replace("|ip|", self.ip) ip_db = mongoInstance.find("ips", {"ip": self.ip}, False) ip_infos = ip_db.get("infos", {}) for info in ip_infos: command = command.replace("|ip.infos." + str(info) + "|", command) if lvl == "port": command = command.replace("|ip|", self.ip) command = command.replace("|port|", self.port) command = command.replace("|port.proto|", self.proto) port_db = mongoInstance.find("ports", { "port": self.port, "proto": self.proto, "ip": self.ip }, False) command = command.replace("|port.service|", port_db["service"]) command = command.replace("|port.product|", port_db["product"]) port_infos = port_db.get("infos", {}) for info in port_infos: # print("replacing "+"|port.infos."+str(info)+"|"+ "by "+str(info)) command = command.replace("|port.infos." + str(info) + "|", str(port_infos[info])) return command
def getCommands(calendarName, worker_name): """ CELERY remote task List worker registered tools in configuration folder. Store the results in mongo database in pollenisator.workers database. """ mongoInstance = MongoCalendar.getInstance() mongoInstance.connectToDb(calendarName) tools_to_register = Utils.loadToolsConfig() print("Registering commands : " + str(list(tools_to_register.keys()))) mongoInstance.registerCommands(worker_name, list(tools_to_register.keys())) return
def searchForAddressCompatibleWithTime(cls): """ Return a list of wave which have at least one interval fitting the actual time. Returns: A set of wave name """ waves_to_launch = set() intervals = Interval.fetchObjects({}) for intervalModel in intervals: if Utils.fitNowTime(intervalModel.dated, intervalModel.datef): waves_to_launch.add(intervalModel.wave) return waves_to_launch
def __init__(self, calendar): """ Constructor. Connect to Celery and start the thread receiving celery worker's events. Args: calendar: the pentest database name to monitor """ dir_path = os.path.dirname(os.path.realpath(__file__)) ssldir = os.path.join(dir_path, "../../ssl/") certs = { 'keyfile': ssldir + 'client.pem', 'certfile': ssldir + 'server.pem', 'ca_certs': ssldir + 'ca.pem', 'cert_reqs': ssl.CERT_REQUIRED } # manager = multiprocessing.Manager() # self.worker_list = manager.dict() dir_path = os.path.dirname(os.path.realpath(__file__)) cfg = Utils.loadClientConfig() userString = cfg["user"]+':'+cfg["password"] + \ '@' if cfg['user'].strip() != "" else "" if cfg["ssl"] == "True": self.app = Celery( 'tasks', broker='mongodb://' + userString + cfg["host"] + ":" + cfg["mongo_port"] + '/broker_pollenisator?authSource=admin&ssl=true&ssl_ca_certs=' + certs["ca_certs"] + '&ssl_certfile=' + certs["keyfile"], connect_timeout=5000) else: self.app = Celery('tasks', broker='mongodb://' + userString + cfg["host"] + ":" + cfg["mongo_port"] + '/broker_pollenisator?authSource=admin', connect_timeout=5000) self.state = self.app.events.State() self.tasks_running = [] self.recv = None self.calendar = calendar # Shared worker list between the child process and main. self.willStop = False self.removeInactiveWorkersTimer = threading.Timer( 30, self.removeInactiveWorkers) self.removeInactiveWorkersTimer.start() self.processEvent = None self.processEvent = threading.Thread( target=self.run, args=(str(calendar), ) ) # This a thread not a process as it needs to catch this app Ctrl+C self.processEvent.start()
def getWaveTimeLimit(waveName): """ Return the latest time limit in which this tool fits. The tool should timeout after that limit Returns: Return the latest time limit in which this tool fits. """ intervals = Interval.fetchObjects({"wave": waveName}) furthestTimeLimit = datetime.now() for intervalModel in intervals: if Utils.fitNowTime(intervalModel.dated, intervalModel.datef): endingDate = intervalModel.getEndingDate() if endingDate is not None: if endingDate > furthestTimeLimit: furthestTimeLimit = endingDate return furthestTimeLimit
def __init__(self, cfg=None): """ Constructor Args: cfg: a dict with keys host, sftp_port, sftp_user, sftp_password. If None, reads configuration file in config/client.cfg Default to None. """ # /home/barre/Documents/Pollenisator/core/Components/FileStorage.py if cfg is None: dir_path = os.path.dirname(os.path.realpath(__file__)) dir_path = os.path.join(dir_path, "../../config/client.cfg") cfg = Utils.loadCfg(dir_path) self.hostname = cfg["host"] self.port = int(cfg["sftp_port"]) self.username = cfg["sftp_user"] self.password = cfg["sftp_password"] self.sftp_connection = None
def delete(self): """ Delete the Interval represented by this model in database. """ mongoInstance = MongoCalendar.getInstance() mongoInstance.delete( "intervals", {"_id": self._id}) parent_wave = mongoInstance.find("waves", {"wave": self.wave}, False) self._id = None if parent_wave is None: return mongoInstance.notify(mongoInstance.calendarName, "waves", parent_wave["_id"], "update", "") other_intervals = Interval.fetchObjects({"wave": self.wave}) no_interval_in_time = True for other_interval in other_intervals: if Utils.fitNowTime(other_interval.dated, other_interval.datef): no_interval_in_time = False break if no_interval_in_time: tools = Tool.fetchObjects({"wave": self.wave}) for tool in tools: tool.setOutOfTime()
def executeCommand(calendarName, toolId, parser=""): """ CELERY remote task Execute the tool with the given toolId on the given calendar name. Then execute the plugin corresponding. Any unhandled exception will result in a task-failed event in the Monitor class. Args: calendarName: The calendar to search the given tool id for. toolId: the mongo Object id corresponding to the tool to execute. parser: plugin name to execute. If empty, the plugin specified in tools.d will be feteched. Raises: Terminated: if the task gets terminated OSError: if the output directory cannot be created (not if it already exists) Exception: if an exception unhandled occurs during the bash command execution. Exception: if a plugin considered a failure. """ # Connect to given calendar mongoInstance = MongoCalendar.getInstance() mongoInstance.connectToDb(calendarName) msg = "" # retrieve tool from tool sid toolModel = Tool.fetchObject({"_id": ObjectId(toolId)}) if toolModel is None: raise Exception("Tool does not exist : " + str(toolId)) command = Command.fetchObject({"name": toolModel.name}, calendarName) # Get time limit and output directory if toolModel.wave == "Custom commands": timeLimit = None else: timeLimit = getWaveTimeLimit(toolModel.wave) if command is not None: timeLimit = min(datetime.now() + timedelta(0, int(command.timeout)), timeLimit) outputRelDir = toolModel.getOutputDir(calendarName) abs_path = os.path.dirname(os.path.abspath(__file__)) outputDir = os.path.join(abs_path, "./results", outputRelDir) # Create the output directory try: os.makedirs(outputDir) except OSError as exc: if exc.errno == errno.EEXIST and os.path.isdir(outputDir): pass else: raise exc # Read Tool config file tools_infos = Utils.loadToolsConfig() comm = toolModel.getCommandToExecute(outputDir) if parser.strip() == "": if toolModel.name not in list(tools_infos.keys()): msg = "TASK FAILED Received tool that was not registered : " + \ str(toolModel.name)+" not in "+str(list(tools_infos.keys())) raise Exception(msg) # Fetch the command to execute if tools_infos.get(toolModel.name, None) is None: bin_path = "" else: bin_path = tools_infos[toolModel.name].get("bin") if bin_path is not None: if not bin_path.endswith(" "): bin_path = bin_path + " " comm = bin_path + comm if comm != "": try: # Load the plugin if parser.strip() == "": mod = Utils.loadPlugin(tools_infos[toolModel.name]["plugin"]) elif parser.strip() == "auto-detect": mod = Utils.loadPluginByBin(toolModel.name.split("::")[0]) else: mod = Utils.loadPlugin(parser) # Complete command with file output toolFileName = toolModel.name+"_" + \ str(time.time())+mod.getFileOutputExt() comm = mod.changeCommand(comm, outputDir, toolFileName) print(('TASK STARTED:' + toolModel.name)) print("Will timeout at " + str(timeLimit)) # Execute the command with a timeout returncode = Utils.execute(comm, timeLimit, True) except Exception as e: raise e # Execute found plugin if there is one if mod is not None: filepath = mod.getFileOutputPath(comm) try: # Open generated file as utf8 with io.open(filepath, "r", encoding="utf-8", errors='ignore') as file_opened: # Check return code by plugin (can be always true if the return code is inconsistent) if mod.checkReturnCode(returncode): notes, tags, _, _ = mod.Parse(file_opened) if notes is None: notes = "No results found by plugin." if tags is None: tags = [] if isinstance(tags, str): tags = [tags] # Success could be change to False by the plugin function (evaluating the return code for exemple) # if the success is validated, mark tool as done toolModel.markAsDone( os.path.join(outputRelDir, os.path.basename(filepath))) # And update the tool in database toolModel.notes = notes toolModel.tags = tags toolModel.update() # Upload file to SFTP mod.centralizeFile(filepath, outputDir) msg = "TASK SUCCESS : " + toolModel.name else: # BAS RESULT OF PLUGIN msg = "TASK FAILED (says the mod) : " + toolModel.name msg += "The return code was not the expected one. (" + str( returncode) + ")." toolModel.markAsError() raise Exception(msg) except IOError as e: toolModel.tags = ["todo"] toolModel.notes = "Failed to read results file" toolModel.markAsDone() else: msg = "TASK FAILED (no plugin found) : " + toolModel.name toolModel.markAsNotDone() raise Exception(msg) # Delay if command is not None: if float(command.sleep_between) > 0.0: msg += " (will sleep for " + \ str(float(command.sleep_between))+")" print(msg) time.sleep(float(command.sleep_between)) return
def initModules(self): discovered_plugins = { name: importlib.import_module(name) for finder, name, ispkg in iter_namespace(core.Components.Modules) } self.modules = [] for name, module in discovered_plugins.items(): module_class = getattr(module, name.split(".")[-1]) module_obj = module_class(self.parent, self.settings) self.modules.append({"name": module_obj.tabName, "object":module_obj, "view":None, "img":ImageTk.PhotoImage(Image.open(Utils.getIconDir()+module_obj.iconName))})
def __init__(self, parent): """ Initialise the application Args: parent: The main tk window. """ # Lexic: # view frame : the frame in the tab that will hold forms. # Tree view : the tree on the left of the window. # frame tree view : a frame around the tree view (useful to attach a scrollbar to a treeview) # canvas : a canvas object (useful to attach a scrollbar to a frame) # paned : a Paned widget is used to separate two other widgets and display a one over the other if desired # Used to separate the treeview frame and view frame. self.parent = parent # parent tkinter window # already read notifications from previous notification reading iteration self.old_notifs = [] self.notifications_timers = None tk.Tk.report_callback_exception = self.show_error self.setStyle() # HISTORY : Main view and command where historically in the same view; # This results in lots of widget here with a confusing naming style ttk.Frame.__init__(self, parent) #### core components (Tab menu on the left objects)#### self.settings = Settings() self.settingViewFrame = None self.scanManager = None # Loaded when clicking on it if linux only self.scanViewFrame = None self.main_tab_img = ImageTk.PhotoImage( Image.open(Utils.getIconDir()+"tab_main.png")) self.commands_tab_img = ImageTk.PhotoImage( Image.open(Utils.getIconDir()+"tab_commands.png")) self.scan_tab_img = ImageTk.PhotoImage( Image.open(Utils.getIconDir()+"tab_scan.png")) self.settings_tab_img = ImageTk.PhotoImage( Image.open(Utils.getIconDir()+"tab_settings.png")) self.initModules() #### MAIN VIEW #### self.openedViewFrameId = None self.mainPageFrame = None self.paned = None self.canvasMain = None self.viewframe = None self.frameTw = None self.treevw = None self.myscrollbarMain = None #### COMMAND VIEW #### self.commandsPageFrame = None self.commandPaned = None self.commandsFrameTw = None self.canvas = None self.commandsViewFrame = None self.myscrollbarCommand = None self.commandsTreevw = None #### SEARCH BAR #### # boolean set to true when the main tree view is displaying search results self.searchMode = False self.searchBar = None # the search bar component self.btnHelp = None # help button on the right of the search bar self.photo = None # the ? image self.helpFrame = None # the floating help frame poping when the button is pressed # Connect to database and choose database to open abandon = False mongoInstance = MongoCalendar.getInstance() while not mongoInstance.isUserConnected() and not abandon: abandon = self.promptForConnection() is None if not abandon: mongoInstance.attach(self) self.initUI() # Will trigger promptForCalendarOpen when tab will be opened else: self.onClosing() try: parent.destroy() except tk.TclError: pass
def doExecuteCommand(workerToken, calendarName, toolId): """ remote task Execute the tool with the given toolId on the given calendar name. Then execute the plugin corresponding. Any unhandled exception will result in a task-failed event in the class. Args: calendarName: The calendar to search the given tool id for. toolId: the mongo Object id corresponding to the tool to execute. parser: plugin name to execute. If empty, the plugin specified in tools.d will be feteched. Raises: Terminated: if the task gets terminated OSError: if the output directory cannot be created (not if it already exists) Exception: if an exception unhandled occurs during the bash command execution. Exception: if a plugin considered a failure. """ apiclient = APIClient.getInstance() apiclient.setToken(workerToken) apiclient.currentPentest = calendarName # bypass login by not using connectToDb toolModel = Tool.fetchObject({"_id":ObjectId(toolId)}) command_dict = toolModel.getCommand() msg = "" success, comm, fileext = apiclient.getCommandline(toolId) if not success: print(str(comm)) toolModel.setStatus(["error"]) return False, str(comm) bin_path = command_dict["bin_path"] if bin_path is not None: if not bin_path.endswith(" "): bin_path = bin_path+" " comm = bin_path+comm outputRelDir = toolModel.getOutputDir(calendarName) abs_path = os.path.dirname(os.path.abspath(__file__)) toolFileName = toolModel.name+"_" + \ str(time.time()) # ext already added in command outputDir = os.path.join(abs_path, "./results", outputRelDir) # Create the output directory try: os.makedirs(outputDir) except OSError as exc: if exc.errno == errno.EEXIST and os.path.isdir(outputDir): pass else: print(str(exc)) toolModel.setStatus(["error"]) return False, str(exc) outputDir = os.path.join(outputDir, toolFileName) comm = comm.replace("|outputDir|", outputDir) # Get tool's wave time limit searching the wave intervals if toolModel.wave == "Custom commands": timeLimit = None else: timeLimit = getWaveTimeLimit(toolModel.wave) # adjust timeLimit if the command has a lower timeout if command_dict is not None: timeLimit = min(datetime.now()+timedelta(0, int(command_dict.get("timeout", 0))), timeLimit) ## if "timedout" in toolModel.status: timeLimit = None try: toolModel.text = comm toolModel.update({"text":comm}) print(('TASK STARTED:'+toolModel.name)) print("Will timeout at "+str(timeLimit)) # Execute the command with a timeout returncode = Utils.execute(comm, timeLimit, True) if returncode == -1: toolModel.setStatus(["timedout"]) return False, str("Command timedout") except Exception as e: print(str(e)) toolModel.setStatus(["error"]) return False, str(e) # Execute found plugin if there is one outputfile = outputDir+fileext plugin = "auto-detect" if command_dict["plugin"] == "" else command_dict["plugin"] msg = apiclient.importToolResult(toolId, plugin, outputfile) if msg != "Success": #toolModel.markAsNotDone() print(str(msg)) toolModel.setStatus(["error"]) return False, str(msg) # Delay if command_dict is not None: if float(command_dict.get("sleep_between", 0)) > 0.0: msg += " (will sleep for " + \ str(float(command_dict.get("sleep_between", 0)))+")" print(msg) time.sleep(float(command_dict.get("sleep_between", 0))) return True, ""
dir_path = os.path.dirname(os.path.realpath(__file__)) # fullpath to this file ssldir = os.path.join(dir_path, "./ssl/") # fullepath to ssl directory certs = { 'keyfile': ssldir + 'client.pem', 'certfile': ssldir + 'server.pem', 'ca_certs': ssldir + 'ca.pem', 'cert_reqs': ssl.CERT_REQUIRED } config_dir = os.path.join(dir_path, "./config/") if not os.path.isfile(os.path.join(config_dir, "client.cfg")): if os.path.isfile(os.path.join(config_dir, "clientSample.cfg")): copyfile(os.path.join(config_dir, "clientSample.cfg"), os.path.join(config_dir, "client.cfg")) if os.path.isfile(os.path.join(config_dir, "client.cfg")): cfg = Utils.loadCfg(os.path.join(config_dir, "client.cfg")) else: print("No client config file found under " + str(config_dir)) sys.exit(1) user_string = cfg["user"]+':'+cfg["password"] + \ '@' if cfg['user'].strip() != "" else "" if cfg["ssl"] == "True": app = Celery( 'tasks', broker='mongodb://' + user_string + cfg["host"] + ':' + cfg["mongo_port"] + '/broker_pollenisator?authSource=admin&ssl=true&ssl_ca_certs=' + certs["ca_certs"] + '&ssl_certfile=' + certs["keyfile"]) else: app = Celery('tasks', broker='mongodb://' + user_string + cfg["host"] + ':' +
def editToolConfig(command_name, remote_bin, plugin): tools_to_register = Utils.loadToolsConfig() tools_to_register[command_name] = {"bin": remote_bin, "plugin": plugin} Utils.saveToolsConfig(tools_to_register)
def openModifyWindow(self): """ Creates a tkinter form using Forms classes. This form aims to update or delete an existing Tool """ modelData = self.controller.getData() top_panel = self.form.addFormPanel(grid=True) top_panel.addFormLabel("Name", modelData["name"]) dates_panel = self.form.addFormPanel(grid=True) dates_panel.addFormLabel("Start date") dates_panel.addFormDate("Start date", self.mainApp, modelData["dated"], column=1) dates_panel.addFormLabel("End date", row=1) dates_panel.addFormDate("End date", self.mainApp, modelData["datef"], row=1, column=1) dates_panel.addFormLabel("Scanner", row=2) dates_panel.addFormStr("Scanner", r"", modelData["scanner_ip"], row=2, column=1) dates_panel.addFormLabel("Command executed", row=3) dates_panel.addFormStr("Command executed", "", modelData["text"], row=3, column=1, state="disabled") notes = modelData.get("notes", "") top_panel = self.form.addFormPanel() top_panel.addFormLabel("Notes", side="top") top_panel.addFormText("Notes", r"", notes, None, side="top", height=15) actions_panel = self.form.addFormPanel() #Ready is legacy, OOS and/or OOT should be used if "ready" in self.controller.getStatus(): actions_panel.addFormButton("Local launch", self.localLaunchCallback, side="right") if self.mainApp.scanManager.monitor.hasWorkers(): actions_panel.addFormButton("Run on worker", self.launchCallback, side="right") else: actions_panel.addFormLabel( "Info", "Tool is ready but no celery worker found", side="right") elif "OOS" in self.controller.getStatus( ) or "OOT" in self.controller.getStatus(): actions_panel.addFormButton("Local launch", self.localLaunchCallback, side="right") if self.mainApp.scanManager.monitor.hasWorkers(): actions_panel.addFormButton("Run on worker", self.launchCallback, side="right") else: actions_panel.addFormLabel( "Info", "Tool is ready but no celery worker found", side="right") elif "running" in self.controller.getStatus(): actions_panel.addFormButton("Stop", self.stopCallback, side="right") elif "done" in self.controller.getStatus(): actions_panel.addFormButton("Download result file", self.downloadResultFile, side="right") tools_infos = Utils.loadToolsConfig() try: mod = Utils.loadPlugin( tools_infos[self.controller.getName()]["plugin"]) pluginActions = mod.getActions(self.controller.model) except KeyError: # Happens when parsed an existing file.: pluginActions = None if pluginActions is not None: for pluginAction in pluginActions: actions_panel.addFormButton(pluginAction, pluginActions[pluginAction], side="right") actions_panel.addFormButton("Reset", self.resetCallback, side="right") defect_panel = self.form.addFormPanel(grid=True) defect_panel.addFormButton("Create defect", self.createDefectCallback) self.completeModifyWindow()
def isDomain(self): """Returns True if this scope is not a valid NetworkIP Returns: bool """ return not Utils.isNetworkIp(self.scope)