示例#1
0
def executeInExternalTerm(command, with_bash=True, env={}):
    from pollenisatorgui.core.Components.Settings import Settings
    settings = Settings()
    favorite = settings.getFavoriteTerm()
    if favorite is None:
        tk.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:
        env = {**os.environ, **env}
        terms = 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:
            if not command_term.endswith(" "):
                command_term += " "
            command_term += command
            subprocess.Popen(command_term,
                             shell=True,
                             env=env,
                             cwd=getExportDir())
        else:
            tk.messagebox.showerror("Terminal settings invalid",
                                    "Check your terminal settings")
    else:
        tk.messagebox.showerror(
            "Terminal settings invalid",
            f"{favorite} terminal is not available on this computer. Choose a different one in the settings module."
        )
    return True
示例#2
0
 def insertIp(self, ip):
     """Insert a new IP in the summary. Also insert its port
     Args:
         ip: an IP object to be inserted
     """
     treevw = ttk.Treeview(self.frameTw)
     treevw.heading("#0", text=ip, anchor='w')
     treevw.column("#0", anchor='w')
     tags = Settings.getTags()
     for tag, color in tags.items():
         treevw.tag_configure(tag, background=color)
     treevw.bind("<Double-Button-1>", self.OnDoubleClick)
     count = 0
     self.treeviews[ip] = treevw
     ports = Port.fetchObjects({"ip": ip})
     for port in ports:
         if port.proto.strip() != "" and str(port.port).strip() != "":
             port_text = port.port
             if port.proto == "udp":
                 port_text = "udp/" + port_text
             treevw.insert('',
                           'end',
                           str(port.getId()),
                           text=port_text,
                           tags=list(port.getTags()))
             count += 1
     treevw.configure(height=count)
     treevw.update_idletasks()
 def configureTags(self):
     self.tag_configure('OOS', background="grey")
     tags = Settings.getTags()
     for tag, color in tags.items():
         try:
             self.tag_configure(tag, background=color)
         except tk.TclError:
             #color does not exist
             pass
示例#4
0
 def openModifyWindow(self):
     """
     Creates a tkinter form using Forms classes. This form aims to update or perform actions on multiple different objects common properties like tags.
     """
     top_panel = self.form.addFormPanel()
     top_panel.addFormButton("Export", self.appliTw.exportSelection)
     top_panel.addFormButton("Hide", self.appliTw.hideSelection)
     top_panel.addFormButton("Custom Command", self.appliTw.customCommand)
     top_panel.addFormButton("Delete", self.appliTw.deleteSelected)
     panTags = self.form.addFormPanel(grid=True)
     registeredTags = Settings.getTags()
     keys = list(registeredTags.keys())
     column = 0
     listOfLambdas = [self.tagClicked(keys[i]) for i in range(len(keys))]
     for registeredTag, color in registeredTags.items():
         s = ttk.Style(self.mainApp)
         s.configure("" + color + ".TButton",
                     background=color,
                     foreground="black",
                     borderwidth=1,
                     bordercolor='#000000')
         s.map("" + color + ".TButton",
               foreground=[('active', "dark gray")],
               background=[('active', color)])
         s.layout("" + color + ".TButton", [('Button.border', {
             'sticky':
             'nswe',
             'border':
             '2',
             'children': [('Button.focus', {
                 'sticky':
                 'nswe',
                 'children': [('Button.padding', {
                     'sticky':
                     'nswe',
                     'children': [('Button.label', {
                         'sticky': 'nswe'
                     })]
                 })]
             })]
         })])
         btn_tag = panTags.addFormButton(registeredTag,
                                         listOfLambdas[column],
                                         column=column)
         btn_tag.configure(style="" + color + ".TButton")
         column += 1
     self.showForm()
示例#5
0
    def openInsertWindow(self):
        """
        Creates a tkinter form using Forms classes. This form aims to insert a new Command
        """
        data = self.controller.getData()
        self._initContextualMenu()
        self.form.addFormHidden("indb", data.get("indb", "pollenisator"))
        panel_top = self.form.addFormPanel(grid=True)
        panel_top.addFormLabel("Name")
        panel_top.addFormStr("Name", r"\S+", data.get("name", ""), None, column=1)
        panel_top.addFormLabel("Level", row=1)
        lvl = ["network", "domain", "ip", "port", "wave"]
        for module in self.mainApp.modules:
            if getattr(module["object"], "registerLvls", False):
                lvl += module["object"].registerLvls
        panel_top.addFormCombo(
            "Level", lvl, data.get("lvl", "network"), row=1, column=1)
        panel_top.addFormHelper(
            "lvl wave: will run on each wave once\nlvl network: will run on each NetworkIP once\nlvl domain: will run on each scope domain once\nlvl ip: will run on each ip/hostname once\nlvl port: will run on each port once", row=1, column=2)
        panel_types = self.form.addFormPanel(grid=True)
        panel_types.addFormChecklist(
            "Types", Settings.getPentestTypes(), data.get("types", []), row=2, column=1)
        panel_types.addFormHelper(
            "This command will be added by default on pentest having a type checked in this list.\nThis list can be modified on settings.", column=2)
        panel_safe = self.form.addFormPanel(grid=True)
        panel_safe.addFormLabel("Safe")
        panel_safe.addFormCheckbox(
            "Safe", "Safe", data.get("safe", False), column=1)
        panel_safe.addFormHelper(
            "If checked, this command can be run by an auto scan.", column=2)
        panel_text = self.form.addFormPanel()
        panel_text.addFormLabel("Command line options", side="top")
        panel_text.addFormHelper(
            """Do not include binary name/path\nDo not include Output file option\nUse variables |wave|, |scope|, |ip|, |port|, |parent_domain|, |outputDir|, |port.service|, |port.product|, |ip.infos.*| |port.infos.*|""", side="right")
        panel_text.addFormText("Command line options",
                               r"", data.get("text", ""), self.menuContextuel, side="top", height=5)
        panel_bottom = self.form.addFormPanel(grid=True)
        panel_bottom.addFormLabel("Ports/Services")
        panel_bottom.addFormStr(
            "Ports/Services", r"^(\d{1,5}|[^\,]+)?(?:,(\d{1,5}|[^\,]+))*$", data.get("ports", ""), width=50, column=1)
        panel_bottom.addFormHelper(
            "Services, ports or port ranges.\nthis list must be separated by a comma, if no protocol is specified, tcp/ will be used.\n Example: ssl/http,https,http/ssl,0-65535,443...", column=2)

        self._commonWindowForms(self.controller.getData())
        self.completeInsertWindow()
示例#6
0
 def _initContextualsMenus(self):
     """
     Create the contextual menu
     """
     self.contextualMenu = tk.Menu(self.parentFrame,
                                   tearoff=0,
                                   background='#A8CF4D',
                                   foreground='black',
                                   activebackground='#A8CF4D',
                                   activeforeground='white')
     self.contextualMenu.add_command(label="Custom command",
                                     command=self.customCommand)
     self.contextualMenu.add_command(label="Export selection",
                                     command=self.exportSelection)
     self.contextualMenu.add_separator()
     for module in self.appli.modules:
         if callable(
                 getattr(module["object"], "_initContextualsMenus", None)):
             menu = module["object"]._initContextualsMenus(self.parentFrame)
             self.contextualMenu.add_cascade(label=module["name"].strip(),
                                             menu=menu)
     self.tagsMenu = tk.Menu(self.parentFrame,
                             tearoff=0,
                             background='#A8CF4D',
                             foreground='white',
                             activebackground='#A8CF4D',
                             activeforeground='white')
     tags = Settings.getTags()
     listOfLambdas = [self.tagClicked(tag) for tag in list(tags.keys())]
     for i, val in enumerate(tags):
         self.tagsMenu.add_command(label=val, command=listOfLambdas[i])
     #self.contextualMenu.add_cascade(label="Tags", menu=self.tagsMenu)
     self.contextualMenu.add_command(label="Sort children",
                                     command=self.sort)
     self.contextualMenu.add_command(label="Expand", command=self.expand)
     self.contextualMenu.add_command(label="Collapse",
                                     command=self.collapse)
     self.contextualMenu.add_command(label="Hide",
                                     command=self.hideAndUpdate)
     self.contextualMenu.add_command(label="Unhide children",
                                     command=self.unhide)
     self.contextualMenu.add_separator()
     self.contextualMenu.add_command(label="Close", command=self.closeMenu)
     return self.contextualMenu
示例#7
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)
        apiclient = APIClient.getInstance()
        scopesOfWave = apiclient.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
示例#8
0
    def openModifyWindow(self):
        """
        Creates a tkinter form using Forms classes. This form aims to update or delete an existing Command
        """
        modelData = self.controller.getData()
        self._initContextualMenu()
        settings = self.mainApp.settings
        settings.reloadSettings()
        self.form.addFormHidden("indb", self.controller.getData()["indb"])
        panel_top = self.form.addFormPanel(grid=True)
        panel_top.addFormLabel("Name", modelData["name"], sticky=tk.NW)
        panel_top.addFormLabel("Level", modelData["lvl"], row=1, sticky=tk.NW)
        panel_top_bis = self.form.addFormPanel(grid=True)
        panel_top_bis.addFormChecklist(
            "Types", Settings.getPentestTypes().keys(), modelData["types"], column=1)
        panel_safe = self.form.addFormPanel(grid=True)
        panel_safe.addFormLabel("Safe")
        panel_safe.addFormCheckbox(
            "Safe", "Safe", modelData["safe"], column=1)
        panel_safe.addFormHelper(
            "If checked, this command can be run by an auto scan.", column=2)
        panel_text = self.form.addFormPanel()
        panel_text.addFormLabel("Command line options", side="top")
        panel_text.addFormHelper(
            """Do not include binary name/path\nDo not include Output file option\nUse variables |wave|, |scope|, |ip|, |port|, |parent_domain|, |outputDir|, |port.service|, |port.product|, |ip.infos.*| |port.infos.*|""", side="right")
        panel_text.addFormText("Command line options", r"",
                               modelData["text"], self.menuContextuel, side="left", height=5)
        panel_bottom = self.form.addFormPanel(grid=True)
        panel_bottom.addFormLabel("Ports/Services", column=0)
        panel_bottom.addFormStr(
            "Ports/Services", r"^(\d{1,5}|[^\,]+)?(?:,(\d{1,5}|[^\,]+))*$", modelData["ports"], self.popup, width=50, column=1)
        panel_bottom.addFormHelper(
            "Services, ports or port ranges.\nthis list must be separated by a comma, if no protocol is specified, tcp/ will be used.\n Example: ssl/http,https,http/ssl,0-65535,443...",column=2)
        panel_bottom = self.form.addFormPanel()

        if not self.controller.isMyCommand():
            panel_bottom.addFormButton("Duplicate to my commands", self.controller.addToMyCommands)
        self._commonWindowForms(modelData)
        self.completeModifyWindow(self.controller.isMyCommand() or self.controller.isWorkerCommand())
示例#9
0
 def refreshUI(self):
     for widget in self.winfo_children():
         try:
             widget.destroy()
         except:
             pass
     self.pack_forget()
     self.registeredTags = Settings.getTags(ignoreCache=True)
     column = 1
     keys = list(self.registeredTags.keys())
     listOfLambdas = [self.tagClicked(keys[i]) for i in range(len(keys))]
     for registeredTag, color in self.registeredTags.items():
         self.tagsCount[registeredTag] = self.tagsCount.get(
             registeredTag, 0)
         try:
             self.labelsTags[registeredTag] = ttk.Label(
                 self,
                 text=registeredTag + " : " +
                 str(self.tagsCount[registeredTag]),
                 relief=tk.SUNKEN,
                 anchor=tk.W,
                 background=color,
                 foreground="black")
         except tk.TclError:
             #color does not exist
             color = "white"
             self.labelsTags[registeredTag] = ttk.Label(
                 self,
                 text=registeredTag + " : " +
                 str(self.tagsCount[registeredTag]),
                 relief=tk.SUNKEN,
                 anchor=tk.W,
                 background=color,
                 foreground="black")
         self.labelsTags[registeredTag].pack(side="left", padx=1)
         self.labelsTags[registeredTag].bind('<Button-1>',
                                             listOfLambdas[column - 1])
         column += 1
示例#10
0
class ActiveDirectory:
    """
    Shows information about ongoing pentest. 
    """
    iconName = "tab_AD.png"
    tabName = "Active Directory"
    collName = "ActiveDirectory"
    settings = Settings()
    registerLvls = []
    running_commands = []

    def __init__(self, parent, settings):
        """
        Constructor
        """
        self.parent = None
        self.users = {}
        self.computers = {}
        self.shares = {}

    @classmethod
    def getSettings(cls):
        return cls.settings.local_settings.get(ActiveDirectory.collName, {})

    @classmethod
    def saveSettings(cls, newSettings):
        cls.settings.local_settings[ActiveDirectory.collName] = newSettings
        cls.settings.saveLocalSettings()

    def open(self):
        apiclient = APIClient.getInstance()
        if apiclient.getCurrentPentest() is not None:
            self.refreshUI()
        return True

    def refreshUI(self):
        """
        Reload data and display them
        """
        self.loadData()
        self.displayData()
        self.reloadSettings()

    def loadData(self):
        """
        Fetch data from database
        """
        apiclient = APIClient.getInstance()
        self.users = apiclient.findInDb(apiclient.getCurrentPentest(),
                                        ActiveDirectory.collName,
                                        {"type": "user"}, True)
        if self.users is None:
            self.users = []
        self.computers = apiclient.findInDb(apiclient.getCurrentPentest(),
                                            ActiveDirectory.collName,
                                            {"type": "computer"}, True)
        if self.computers is None:
            self.computers = []
        self.shares = apiclient.findInDb(apiclient.getCurrentPentest(),
                                         ActiveDirectory.collName,
                                         {"type": "share"}, True)
        if self.shares is None:
            self.shares = []
        ports = Port.fetchObjects({"port": str(445)})
        for port in ports:
            self.loadInfoFromPort(port)
        ports = Port.fetchObjects({"port": str(88)})
        for port in ports:
            self.addDCinDb(port.ip)

    def displayData(self):
        """
        Display loaded data in treeviews
        """
        dialog = ChildDialogProgress(
            self.parent, "Loading infos ",
            "Refreshing infos. Please wait for a few seconds.", 200,
            "determinate")
        dialog.show(3)

        self.tvUsers.reset()
        self.tvComputers.reset()
        self.tvShares.reset()
        dialog.update(1)
        for user in self.users:
            self.insertUser(user)
        for computer in self.computers:
            self.insertComputer(computer)
        for share in self.shares:
            self.insertShare(share)
        dialog.update(3)
        dialog.destroy()

    def mapLambda(self, c):
        if c[0].lower() in [
                "computer", "oncomputernewuser", "oncomputerfirstuser",
                "oncomputerfirstadmin", "ondcfirstuser", "ondcfirstadmin"
        ]:
            return lambda: self.computerCommand(c[2])
        elif c[0].lower() == "share":
            return lambda: self.shareCommand(c[2])

    def reloadSettings(self):
        s = self.getSettings()
        listOfLambdas = [self.mapLambda(c) for c in s.get("commands", [])]
        i = 0
        for command_options in s.get("commands", []):
            if command_options[0].lower() in [
                    "computer", "oncomputernewuser", "oncomputerfirstuser",
                    "oncomputerfirstadmin", "ondcfirstuser", "ondcfirstadmin"
            ]:
                self.tvComputers.addContextMenuCommand(command_options[1],
                                                       listOfLambdas[i])
            elif command_options[0].lower() == "share":
                self.tvShares.addContextMenuCommand(command_options[1],
                                                    listOfLambdas[i])
            i += 1

    def initUI(self, parent, nbk, treevw, tkApp):
        """
        Initialize Dashboard widgets
        Args:
            parent: its parent widget
        """
        if self.parent is not None:  # Already initialized
            return
        self.parent = parent
        self.tkApp = tkApp
        self.treevwApp = treevw
        settings_btn = ttk.Button(parent,
                                  text="Configure this module",
                                  command=self.openConfig)
        settings_btn.pack(side="bottom")
        self.moduleFrame = ttk.Frame(parent)
        frameUsers = ttk.Frame(self.moduleFrame)
        self.tvUsers = ScrollableTreeview(
            frameUsers,
            ("Username", "Password", "Domain", "N° groups", "Desc"),
            binds={
                "<Delete>": self.deleteUser,
                "<Double-Button-1>": self.userDoubleClick
            })
        self.tvUsers.pack(fill=tk.BOTH)
        addUserButton = ttk.Button(frameUsers,
                                   text="Add user manually",
                                   command=self.addUsersDialog)
        addUserButton.pack(side="bottom")
        frameComputers = ttk.Frame(self.moduleFrame)
        self.tvComputers = ScrollableTreeview(
            frameComputers, ("IP", "Name", "Domain", "DC", "Admin count",
                             "User count", "OS", "Signing", "SMBv1"),
            binds={"<Double-Button-1>": self.computerDoubleClick})
        self.tvComputers.pack(fill=tk.BOTH)
        frameShares = ttk.Frame(self.moduleFrame)
        self.tvShares = ScrollableTreeview(
            frameShares,
            ("IP", "Share", "Flagged", "Priv", "Size", "domain", "user"))
        self.tvShares.pack(fill=tk.BOTH)
        frameUsers.grid(row=0, column=0)
        frameComputers.grid(row=1, column=0)
        frameShares.grid(row=2, column=0)
        self.moduleFrame.columnconfigure(0, weight=1)
        self.moduleFrame.columnconfigure(1, weight=1)
        self.moduleFrame.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)

    def computerDoubleClick(self, event=None):
        selection = self.tvComputers.selection()[0]
        self.openComputerDialog(selection)

    def openComputerDialog(self, computer_iid):
        apiclient = APIClient.getInstance()
        computer_d = apiclient.findInDb(apiclient.getCurrentPentest(),
                                        ActiveDirectory.collName,
                                        {"_id": ObjectId(computer_iid)}, False)
        if computer_d is None:
            return
        dialog = ChildDialogComputer(self.parent, computer_d)
        self.parent.wait_window(dialog.app)

    def insertUser(self, user):
        try:
            domain = user.get("domain", "")
            username = user.get("username", "")
            password = user.get("password", "")
            self.tvUsers.insert('',
                                'end',
                                user["_id"],
                                text=username,
                                values=(password, domain,
                                        len(user.get("groups", [])),
                                        user.get("desc", "")))
            self.newUserEvent("OnUserInsert",
                              user=(domain, username, password))
        except tk.TclError:
            pass

    def userDoubleClick(self, event=None):
        selection = self.tvUsers.selection()[0]
        self.openUserDialog(selection)

    def openUserDialog(self, user_iid):
        apiclient = APIClient.getInstance()
        user_d = apiclient.findInDb(apiclient.getCurrentPentest(),
                                    ActiveDirectory.collName,
                                    {"_id": ObjectId(user_iid)}, False)
        if user_d is None:
            return
        dialog = ChildDialogUser(self.parent, user_d)
        self.parent.wait_window(dialog.app)

    def deleteUser(self, event=None):
        apiclient = APIClient.getInstance()
        selection = self.tvUsers.selection()
        for select in selection:
            try:
                item = self.tvUsers.item(select)
            except tk.TclError:
                pass
            username = item["text"]
            values = item["values"]
            password = values[0]
            domain = values[
                1] if values[1] != "None" and values[1] != "" else None
            computers = apiclient.findInDb(
                apiclient.getCurrentPentest(), ActiveDirectory.collName, {
                    "type": "computer",
                    "users": {
                        "$elemMatch": {
                            "0": domain,
                            "1": username,
                            "2": password
                        }
                    }
                }, True)
            for computer in computers:
                users = [
                    u for u in computer.get("users")
                    if domain != u[0] and username != u[1] and username != u[2]
                ]
                admins = [
                    u for u in computer.get("admins")
                    if domain != u[0] and username != u[1] and username != u[2]
                ]
                apiclient.updateInDb(
                    apiclient.getCurrentPentest(),
                    ActiveDirectory.collName,
                    {"_id": ObjectId(computer["_id"])},
                    {"$set": {
                        "users": users,
                        "admins": admins
                    }},
                    notify=True)
            apiclient.deleteFromDb(apiclient.getCurrentPentest(),
                                   ActiveDirectory.collName,
                                   {"_id": ObjectId(select)},
                                   notify=True)
            self.tvUsers.delete(select)

    def insertComputer(self, computer):
        newValues = (computer.get("name", computer.get("machine_name", "")),
                     computer.get("domain",
                                  ""), str(computer.get("isDC", False)),
                     len(computer.get("admins", [])),
                     len(computer.get("users", [])), computer.get("OS", ""),
                     computer.get("signing", ""), computer.get("SMBv1", ""))
        try:
            self.tvComputers.insert('',
                                    'end',
                                    computer["_id"],
                                    text=computer.get("ip", ""),
                                    values=newValues)
        except tk.TclError:
            self.tvComputers.item(computer["_id"], values=newValues)

    def insertShare(self, share):
        try:
            parentiid = self.tvShares.insert('',
                                             'end',
                                             share["_id"],
                                             text=share.get("ip", ""),
                                             values=(share.get("name", ""), ))
        except tk.TclError:
            parentiid = str(share["_id"])
        for file_infos in share.get("files"):
            toAdd = []
            for info in file_infos:
                if info is None:
                    info = ""
                toAdd.append(str(info))
            try:
                self.tvShares.insert(parentiid,
                                     'end',
                                     None,
                                     text="",
                                     values=(tuple(toAdd)))
            except tk.TclError:
                pass

    def addShareInDb(self, share_dict, port_o):
        apiclient = APIClient.getInstance()
        for shareName, path_infos in share_dict.items():
            existing = apiclient.findInDb(apiclient.getCurrentPentest(),
                                          ActiveDirectory.collName, {
                                              "type": "share",
                                              "name": shareName,
                                              "ip": port_o.ip
                                          }, False)
            if existing is None:
                apiclient.insertInDb(apiclient.getCurrentPentest(),
                                     ActiveDirectory.collName, {
                                         "type": "share",
                                         "name": shareName,
                                         "ip": port_o.ip,
                                         "files": path_infos
                                     },
                                     notify=True)
            else:
                files = set(map(tuple, existing["files"]))
                files = list(files.union(set(map(tuple, path_infos))))
                apiclient.updateInDb(apiclient.getCurrentPentest(),
                                     ActiveDirectory.collName,
                                     {"_id": existing["_id"]}, {
                                         "$set": {
                                             "type": "share",
                                             "name": shareName,
                                             "ip": port_o.ip,
                                             "files": files
                                         }
                                     })

    @staticmethod
    def normalize_users(user_records):
        ret = []
        for user_record in user_records:
            if isinstance(user_record, dict):
                domain = user_record.get("domain", "")
                username = user_record.get("username", "")
                password = user_record.get("password", "")
                ret.append([domain, username, password])
            elif isinstance(user_record, list) or isinstance(
                    user_record, tuple):
                ret.append(user_record)
        return ret

    def addUserInDb(self, user):
        apiclient = APIClient.getInstance()
        list_norm_user = ActiveDirectory.normalize_users([user])
        if list_norm_user:
            norm_user = list_norm_user[0]
            if len(norm_user) == 3:
                domain = norm_user[0]
                username = norm_user[1]
                password = norm_user[2]
                key = {
                    "type": "user",
                    "username": username,
                    "domain": domain,
                    "password": password
                }
                data = key
                res = apiclient.updateInDb(apiclient.getCurrentPentest(),
                                           ActiveDirectory.collName,
                                           key, {"$set": data},
                                           notify=True,
                                           upsert=True)

    def addDomainUserInDb(self, user_login, user_infos):
        apiclient = APIClient.getInstance()
        domain = user_login.split("\\")[0]
        username = "******".join(user_login.split("\\")[1:])
        key = {"type": "user", "username": username, "domain": domain}
        data = user_infos
        apiclient.updateInDb(apiclient.getCurrentPentest(),
                             ActiveDirectory.collName,
                             key, {"$set": data},
                             notify=True,
                             upsert=True)

    def addDCinDb(self, ip):
        apiclient = APIClient.getInstance()
        existing = apiclient.findInDb(apiclient.getCurrentPentest(),
                                      ActiveDirectory.collName, {
                                          "type": "computer",
                                          "ip": ip
                                      }, False)
        if existing is None:
            apiclient.insertInDb(apiclient.getCurrentPentest(),
                                 ActiveDirectory.collName, {
                                     "type": "computer",
                                     "domain": "",
                                     "isDC": True,
                                     "name": "",
                                     "ip": ip,
                                     "OS": "",
                                     "signing": True,
                                     "SMBv1": "",
                                     "users": [],
                                     "admins": []
                                 },
                                 notify=True)
        else:
            apiclient.updateInDb(apiclient.getCurrentPentest(),
                                 ActiveDirectory.collName,
                                 {"_id": ObjectId(existing["_id"])},
                                 {"$set": {
                                     "isDC": True
                                 }},
                                 notify=True)
            if not existing.get("isDC", False):
                if len(existing.get("users", [])) > 0:
                    self.newComputerEvent("OnDCFirstUser",
                                          existing.get("users", [])[0], ip)
                if len(existing.get("admins", [])) > 0:
                    self.newComputerEvent("OnDCFirstAdmin",
                                          existing.get("admins", [])[0], ip)

    def addComputerinDb(self, port_o):
        apiclient = APIClient.getInstance()
        existing = apiclient.findInDb(apiclient.getCurrentPentest(),
                                      ActiveDirectory.collName, {
                                          "type": "computer",
                                          "ip": port_o.ip
                                      }, False)
        existingUsers = []
        newUsers = []
        existingAdmins = []
        newAdmins = []
        isDC = False
        if existing is None:
            newUsers = ActiveDirectory.normalize_users(
                port_o.infos.get("users", []))
            newAdmins = ActiveDirectory.normalize_users(
                port_o.infos.get("admins", []))
            apiclient.insertInDb(
                apiclient.getCurrentPentest(),
                ActiveDirectory.collName, {
                    "type": "computer",
                    "domain": port_o.infos.get("domain", ""),
                    "isDC": isDC,
                    "name": port_o.infos.get("machine_name", ""),
                    "ip": port_o.ip,
                    "OS": port_o.infos.get("OS", ""),
                    "signing": port_o.infos.get("signing", ""),
                    "secrets": port_o.infos.get("secrets", ""),
                    "ntds": port_o.infos.get("ntds", ""),
                    "SMBv1": port_o.infos.get("SMBv1", ""),
                    "users": newUsers,
                    "admins": newAdmins
                },
                notify=True)
        else:
            isDC = existing.get("isDC", False)
            existingUsers = set(
                map(tuple,
                    ActiveDirectory.normalize_users(existing.get("users",
                                                                 []))))
            newUsers = list(
                existingUsers.union(
                    map(
                        tuple,
                        ActiveDirectory.normalize_users(
                            port_o.infos.get("users", [])))))
            existingAdmins = set(
                map(
                    tuple,
                    ActiveDirectory.normalize_users(existing.get("admins",
                                                                 []))))
            newAdmins = list(
                existingAdmins.union(
                    map(
                        tuple,
                        ActiveDirectory.normalize_users(
                            port_o.infos.get("admins", [])))))
            apiclient.updateInDb(
                apiclient.getCurrentPentest(), ActiveDirectory.collName,
                {"_id": ObjectId(existing["_id"])}, {
                    "$set": {
                        "name": port_o.infos.get("machine_name", ""),
                        "domain": port_o.infos.get("domain", ""),
                        "isDC": isDC,
                        "OS": port_o.infos.get("OS", ""),
                        "signing": port_o.infos.get("signing", ""),
                        "secrets": port_o.infos.get("secrets", ""),
                        "ntds": port_o.infos.get("ntds", ""),
                        "SMBv1": port_o.infos.get("SMBv1", ""),
                        "users": newUsers,
                        "admins": newAdmins
                    }
                })
        self.checkEvents(port_o, existingUsers, newUsers, existingAdmins,
                         newAdmins, isDC)

    def checkEvents(self, port_o, existingUsers, newUsers, existingAdmins,
                    newAdmins, isDC):
        if len(newUsers) > len(existingUsers):
            for user in set(newUsers) - set(existingUsers):
                self.newComputerEvent("OnComputerNewUser", user, port_o.ip)
        if len(newAdmins) >= 1 and len(existingAdmins) == 0:
            admin = list(set(newAdmins) - set(existingAdmins))[0]
            self.newComputerEvent("OnComputerFirstAdmin", admin, port_o.ip)
            if isDC:
                self.newComputerEvent("OnDCFirstAdmin", admin, port_o.ip)
        if len(newUsers) > 0:
            realNewUsers = set()
            for user in newUsers:
                if not any(
                        map(lambda x: x is None or x == '' or x == 'None',
                            user)):
                    realNewUsers.add(user)
            if len(realNewUsers) > 0:
                oneExistingUser = False
                incompleteUsers = set()
                for user in existingUsers:
                    if not any(
                            map(lambda x: x is None or x == '' or x == 'None',
                                user)):
                        oneExistingUser = True
                        break
                    else:
                        incompleteUsers.add(user)
                if not oneExistingUser:
                    self.newComputerEvent(
                        "OnComputerFirstUser",
                        list(set(newUsers) - incompleteUsers)[0], port_o.ip)
                    if isDC:
                        self.newComputerEvent(
                            "OnDCFirstUser",
                            list(set(newUsers) - incompleteUsers)[0],
                            port_o.ip)

    def newComputerEvent(self, eventName, user, ip):
        apiclient = APIClient.getInstance()
        existing = apiclient.findInDb(apiclient.getCurrentPentest(),
                                      ActiveDirectory.collName, {
                                          "type": "computer",
                                          "ip": ip
                                      }, False)
        if existing is None:
            return
        s = self.getSettings()
        for command_options in s.get("commands", []):
            if command_options[0].lower() == eventName.lower():
                self.computerCommand(command_options[2],
                                     ips=[existing["ip"]],
                                     user=user)

    def newUserEvent(self, eventName, user):
        s = self.getSettings()
        for command_options in s.get("commands", []):
            if command_options[0].lower() == eventName:
                self.userCommand(command_options[2], user=user)

    def notify(self, db, collection, iid, action, _parent):
        """
        Callback for the observer implemented in mongo.py.
        Each time an object is inserted, updated or deleted the standard way, this function will be called.

        Args:
            collection: the collection that has been modified
            iid: the mongo ObjectId _id that was modified/inserted/deleted
            action: string "update" or "insert" or "delete". It was the action performed on the iid
            _parent: Not used. the mongo ObjectId of the parent. Only if action in an insert. Not used anymore
        """
        apiclient = APIClient.getInstance()
        if not apiclient.getCurrentPentest() != "":
            return
        if apiclient.getCurrentPentest() != db:
            return
        if collection == ActiveDirectory.collName:
            self.handleActiveDirectoryNotif(iid, action)
        elif collection == "ports" and action != "delete":
            port_o = Port.fetchObject({"_id": ObjectId(iid)})
            if int(port_o.port) == 445:
                self.loadInfoFromPort(port_o)
            if int(port_o.port) == 88:
                self.addDCinDb(port_o.ip)

    def loadInfoFromPort(self, port_o):
        if port_o.infos.get("users", []):
            for user in port_o.infos.get("users", []):
                self.addUserInDb(user)
        if port_o.infos.get("domain_users", []):
            for user_login, user_infos in port_o.infos.get("domain_users",
                                                           {}).items():
                self.addDomainUserInDb(user_login, user_infos)
        if port_o.infos.get("admins", []):
            for admin in port_o.infos.get("admins", []):
                self.addUserInDb(admin)
        self.addComputerinDb(port_o)
        if port_o.infos.get("shares", {}):
            self.addShareInDb(port_o.infos.get("shares", {}), port_o)

    def handleActiveDirectoryNotif(self, iid, action):
        apiclient = APIClient.getInstance()
        res = apiclient.findInDb(apiclient.getCurrentPentest(),
                                 ActiveDirectory.collName,
                                 {"_id": ObjectId(iid)}, False)
        if action == "insert":
            if res is None:
                return
            if res["type"] == "computer":
                self.insertComputer(res)
            elif res["type"] == "user":
                self.insertUser(res)
            elif res["type"] == "share":
                self.insertShare(res)
        elif action == "update":
            if res is None:
                return
            if res["type"] == "computer":
                self.insertComputer(res)
            if res["type"] == "share":
                self.insertShare(res)
        elif action == "delete":
            if res is None:
                return
            try:
                if res["type"] == "computer":
                    self.tvComputers.delete(str(iid))
                elif res["type"] == "user":
                    self.tvUsers.delete(str(iid))
                elif res["type"] == "share":
                    self.tvShares.delete(str(iid))
            except tk.TclError:
                pass

    def openConfig(self, event=None):
        dialog = ChildDialogConfigureADModule(self.parent)
        self.parent.wait_window(dialog.app)
        if dialog.rvalue is not None and isinstance(dialog.rvalue, list):
            settings = {"commands": []}
            for values in dialog.rvalue:
                settings["commands"].append(values)
            self.saveSettings(settings)
        self.reloadSettings()

    def addUsersDialog(self, event=None):
        dialog = ChildDialogAddUsers(self.parent)
        self.parent.wait_window(dialog.app)
        if dialog.rvalue is not None and isinstance(dialog.rvalue, list):
            lines = dialog.rvalue
            for line in lines:
                if line.strip() != "":
                    parts = line.split("\\")
                    domain = parts[0]
                    remaining = "\\".join(parts[1:])
                    parts = remaining.split(":")
                    username = parts[0]
                    password = "******".join(parts[1:])
                    self.addUserInDb((domain, username, password))

    def exportAllUsersAsFile(self, delim="\n"):
        fp = tempfile.mkdtemp()
        filepath = os.path.join(fp, "users.txt")
        with open(filepath, "w") as f:
            for selected_user in self.tvUsers.get_children():
                username = self.tvUsers.item(selected_user)["text"]
                f.write(username + delim)
        return filepath

    def exportAllComputersAsFile(self, delim="\n"):
        fp = tempfile.mkdtemp()
        filepath = os.path.join(fp, "computers.txt")
        with open(filepath, "w") as f:
            for selected_computer in self.tvComputers.get_children():
                computer = self.tvComputers.item(selected_computer)["text"]
                f.write(computer + delim)
        return filepath

    def shareCommand(self, command_option):
        selected = self.tvShares.selection()[0]
        parent_iid = self.tvShares.parent(selected)
        if not parent_iid:  # file in share
            return
        ip = self.tvShares.item(parent_iid)["text"]
        item_values = self.tvShares.item(selected)["values"]
        domain = item_values[-2] if item_values[-2] != "" else None
        user = item_values[-1]
        share_name = item_values[0]
        apiclient = APIClient.getInstance()
        apiclient.getCurrentPentest()
        user_o = apiclient.findInDb(apiclient.getCurrentPentest(),
                                    ActiveDirectory.collName, {
                                        "type": "user",
                                        "domain": domain,
                                        "username": user
                                    }, False)
        if user_o is None:
            tk.messagebox.showerror(
                "user not found",
                "User " + str(domain) + "\\" + str(user) + " was not found")
            return
        user = "" if user is None else user
        domain = "" if domain is None else domain
        command_option = command_option.replace("|username|", user)
        command_option = command_option.replace("|domain|", domain)
        command_option = command_option.replace("|password|",
                                                user_o["password"])
        command_option = command_option.replace(
            "|share|", share_name.replace("\\\\", "\\"))
        command_option = command_option.replace("|ip|", ip)
        Utils.executeInExternalTerm(command_option)

    def userCommand(self, command_option, user):
        if user is None:
            selection_users = self.tvUsers.selection()
            if len(selection_users) >= 1:
                item = self.tvUsers.item(selection_users[0])
                user = (item["values"][1], item["text"], item["values"][0])
            else:
                user = None
        searching = [r"ask_text:([^:\|]+)", "computers_as_file"]
        for keyword in searching:
            s = re.search(r"\|" + keyword + r"\|", command_option)
            if s is not None:
                if keyword == "computers_as_file":
                    filepath = self.exportAllComputersAsFile()
                    command_option = command_option.replace(
                        s.group(0), filepath)
                elif "|ask_text:" in s.group(0):
                    what = s.group(1)
                    dialog = ChildDialogAskText(self.tkApp, what)
                    self.tkApp.wait_window(dialog.app)
                    fp = tempfile.mkdtemp()
                    filepath = os.path.join(fp, what + ".txt")
                    with open(filepath, "w") as f:
                        f.write(dialog.rvalue)
                    command_option = command_option.replace(
                        s.group(0), filepath)
            command_option = command_option.replace("|domain|", user[0])
            command_option = command_option.replace("|username|", user[1])
            command_option = command_option.replace("|password|", user[2])
        Utils.executeInExternalTerm(command_option)

    def computerCommand(self, command_option, ips=None, user=None):
        if ips is None:
            ips = []
            selection = self.tvComputers.selection()
            for selected in selection:
                item = self.tvComputers.item(selected)
                ip = item["text"]
                ips.append(ip)
        if user is None:
            selection_users = self.tvUsers.selection()
            if len(selection_users) >= 1:
                item = self.tvUsers.item(selection_users[0])
                user = (item["values"][1], item["text"], item["values"][0])
            else:
                user = None
        for ip in ips:
            searching = [
                "wordlist", r"ask_text:([^:\|]+)", "users_as_file", "ip"
            ]
            for keyword in searching:
                s = re.search(r"\|" + keyword + r"\|", command_option)
                if s is not None:
                    if keyword == "wordlist":
                        dialog = ChildDialogAskFile(self.tkApp,
                                                    f"Choose a wordlist file")
                        command_option = command_option.replace(
                            s.group(0), dialog.rvalue)
                    elif keyword == "ip":
                        command_option = command_option.replace(s.group(0), ip)
                    elif keyword == "users_as_file":
                        filepath = self.exportAllUsersAsFile()
                        command_option = command_option.replace(
                            s.group(0), filepath)
                    elif "|ask_text:" in s.group(0):
                        what = s.group(1)
                        dialog = ChildDialogAskText(self.tkApp, what)
                        self.tkApp.wait_window(dialog.app)
                        fp = tempfile.mkdtemp()
                        filepath = os.path.join(fp, what + ".txt")
                        with open(filepath, "w") as f:
                            f.write(dialog.rvalue)
                        command_option = command_option.replace(
                            s.group(0), filepath)
            user_searching = {
                "domain": None,
                "username": None,
                "password": None
            }
            if any(
                    map(
                        lambda user_keyword: f"|{user_keyword}|" in
                        command_option, user_searching)):
                if user is not None and len(user) == 3:
                    command_option = command_option.replace(
                        "|domain|", user[0])
                    command_option = command_option.replace(
                        "|username|", user[1])
                    command_option = command_option.replace(
                        "|password|", user[2])
                else:
                    for user_keyword in user_searching:
                        if f"|{user_keyword}|" in command_option:
                            dialog = ChildDialogAskText(self.parent,
                                                        "Enter " +
                                                        str(user_keyword),
                                                        multiline=False)
                            self.parent.wait_window(dialog.app)
                            if dialog.rvalue is None:
                                return
                            command_option = command_option.replace(
                                f"|{user_keyword}|", dialog.rvalue)
            Utils.executeInExternalTerm(command_option)

    def ask_confirmation(self, title, question):
        dialog = ChildDialogQuestion(self.parent, title, question)
        self.parent.wait_window(dialog.app)
        return dialog.rvalue == "Yes"