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
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
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()
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()
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
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
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())
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
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"