Пример #1
0
    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
Пример #2
0
    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)
Пример #3
0
 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
Пример #4
0
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()
Пример #5
0
 def resetUnfinishedTools(self):
     """
     Reset all running tools to a ready state.
     """
     mongoInstance = MongoCalendar.getInstance()
     if mongoInstance.hasACalendarOpen():
         Utils.resetUnfinishedTools()
         self.treevw.load()
Пример #6
0
    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
Пример #7
0
    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"))
Пример #8
0
 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)
Пример #9
0
 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()
Пример #10
0
    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
Пример #11
0
 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
Пример #12
0
 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")
Пример #13
0
 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()
Пример #14
0
 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')
Пример #15
0
 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
Пример #16
0
 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
Пример #17
0
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
Пример #18
0
    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
Пример #19
0
    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()
Пример #20
0
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
Пример #21
0
 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
Пример #22
0
 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()
Пример #23
0
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
Пример #24
0
 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))})
Пример #25
0
    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
Пример #26
0
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, ""
Пример #27
0
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"] + ':' +
Пример #28
0
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)
Пример #29
0
    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()
Пример #30
0
 def isDomain(self):
     """Returns True if this scope is not a valid NetworkIP
     Returns:
         bool
     """
     return not Utils.isNetworkIp(self.scope)