def update_machines(db_filename, htb_username, htb_password, htb_api): log = Lumberjack("log/update_machines_events.log", True) db = SQLWizard(db_filename) htb_helper = HTBhelper(htb_username, htb_password, htb_api) if not htb_helper.login(): log.error("ERROR: Failed to login") sys.exit(1) log.info("Getting all machines") all_machines_json = htb_helper.get_all_machines() if not all_machines_json: log.error("ERROR: Failed to get machines json") sys.exit(1) all_machines_json = json.loads(all_machines_json) for box in all_machines_json: box_name = box["name"].strip().lower() if db.select("name", "boxes", "name = '{}'".format(box_name)): log.debug("Box {} already in the database".format(box_name)) continue box_image = box["avatar_thumb"].replace("_thumb", "") box_id = box["id"] log.info("Getting {} ({}) box score".format(box_name, box_id)) box_score = htb_helper.get_machine_score(box_id) log.info("Adding new box {} {}".format(box_name, box_score)) db.insert( "boxes", OrderedDict([("name", box_name), ("score", box_score), ("image", box_image), ("wts", "-"), ("working", "-"), ("user", "-"), ("root", "-")]))
class PWNgress(discord.Client): """ PWNgress. """ def __init__(self, discord_token, db_filename, notifications=False): self.log = Lumberjack("log/PWNgress_events.log", True) self.db = SQLWizard(db_filename) self.notifications = notifications super().__init__() self.run(discord_token) async def on_ready(self): """ Print state when PWNgress is ready. If class started with notification parameter, send the messages and kill the process. """ self.log.info("PWNgress started") if self.notifications: for notification in self.notifications: await self.send_pwn_notification(*notification) sys.exit(1) async def on_message(self, message): """ Parse any message except own. If message starts with .status - delete it and return information about the box. """ if self.notifications: return False if message.author == self.user: return False channel_name = message.channel.name.lower() self.log.info("Processing message from {} on {} channel".format(str(message.author), channel_name)) if channel_name != "pwngress": return False self.log.debug("Message content: " + message.content) if not message.content.startswith(".status"): return False htb_name = self.get_htb_name(str(message.author)) if not htb_name: return False await message.channel.delete_messages([message]) if len(message.content.split(" ")) == 2: box_name = message.content.split(" ")[1] box_embed_info = self.get_box_embed_info(box_name) if not box_embed_info: self.log.error("Creating box embed information failed for " + box_name) return False self.log.info("Sent embed message for " + box_name) await message.channel.send("", embed=box_embed_info) return True if len(message.content.split(" ")) == 3: box_name = message.content.split(" ")[1].lower() box_status = self.get_box_status_from_user_input(message.content.split(" ")[2]) box_data = self.db.select("*", "boxes", "name = '{}'".format(box_name)) if not box_data: self.log.error("Box was not found - " + box_name) return False # TODO: Change this new_data = {} if box_data[0][3]: wts = box_data[0][3].replace(htb_name + ",", "").replace(htb_name, "") else: wts = "-" if box_data[0][4]: working = box_data[0][4].replace(htb_name + ",", "").replace(htb_name, "") else: working = "-" if box_data[0][5]: user = box_data[0][5].replace(htb_name + ",", "").replace(htb_name, "") else: user = "******" if box_data[0][6]: root = box_data[0][6].replace(htb_name + ",", "").replace(htb_name, "") else: root = "-" new_data["wts"] = wts if wts else "-" new_data["working"] = working if working else "-" new_data["user"] = user if user else "-" new_data["root"] = root if root else "-" if new_data[box_status] and new_data[box_status] != "-": new_data[box_status] += "," + htb_name else: new_data[box_status] = htb_name self.db.update( "boxes", OrderedDict([ ("wts", new_data["wts"]), ("working", new_data["working"]), ("user", new_data["user"]), ("root", new_data["root"]) ]), "name = '{}'".format(box_name) ) self.log.info("Box status updated - " + box_name) # Send messages to WTS group if box_status == "wts": wts_users = new_data["wts"].replace(htb_name + ",", "").replace(htb_name, "") if wts_users: for wts_user in wts_users.split(","): if not wts_user.strip() or wts_user == str(message.author) or wts_user.strip() == "-": continue discord_id = self.get_discord_id(wts_user) user_obj = self.get_user(int(discord_id)) message_data = "Hi {}. {} wants to start pwning {} box :)".format(wts_user, message.author, box_name) await user_obj.send(message_data) self.log.info("Sent message to {} ({})".format(wts_user, discord_id)) return True return False @staticmethod def get_box_status_from_user_input(box_status): """ Get correct status of the box. """ if box_status.lower() in ["wts", "s"]: return "wts" if box_status.lower() in ["working", "w"]: return "working" if box_status.lower() in ["user", "u"]: return "user" if box_status.lower() in ["root", "r"]: return "root" return False def create_new_box(self, box_name, box_score="?/10", box_image="https://www.hackthebox.eu/images/favicon.png", wts="", working="", user="", root=""): """ Create new box in the database. """ self.db.insert( "boxes", OrderedDict([ ("name", box_name), ("score", box_score), ("image", box_image), ("wts", wts), ("working", working), ("user", user), ("root", root) ]) ) def get_htb_name(self, discord_name): """ Get HTB name of the user. """ htb_name = self.db.select("*", "names", "discord_name = '{}'".format(str(discord_name).lower())) if htb_name: self.log.debug("HTB name: " + htb_name[0][1]) return htb_name[0][1].lower() self.log.error("HTB name was not found - " + discord_name) return False def get_discord_name(self, htb_name): """ Get Discord name of the user. """ discord_name = self.db.select("*", "names", "htb_name = '{}'".format(htb_name.lower())) if discord_name: self.log.debug("Discord name: " + discord_name[0][2].lower()) return discord_name[0][2] self.log.error("Discord name was not found - " + htb_name.lower()) return False def get_discord_id(self, name): """ Get Discord ID of the user. """ discord_id = self.db.select("*", "names", "discord_name = '{name}' or htb_name = '{name}'".format(name=name.lower())) if discord_id: self.log.debug("Discord ID: " + discord_id[0][0].lower()) return discord_id[0][0].lower() self.log.error("Discord ID was not found - " + name.lower()) return False @staticmethod def split_usernames(username_list): """ Add new lines after each two usernames. Return "-" if empty """ if not username_list: return "-" if "," in username_list: output = "" for index, usernames in enumerate(username_list.split(",")): output += usernames if (index - 1) % 2 == 0: output += "\n" else: output += " " return output return username_list def get_box_embed_info(self, box_name): """ Get status information for the box by box name. """ box_data = self.db.select("*", "boxes", "name = '{}'".format(box_name)) if box_data: embed = discord.Embed(title=box_data[0][0].upper(), description=box_data[0][1], color=0x2B78E3) embed.set_thumbnail(url=box_data[0][2]) embed.add_field(name="W.T.S.", value=self.split_usernames(box_data[0][3]), inline=True) embed.add_field(name="WORKING", value=self.split_usernames(box_data[0][4]), inline=True) embed.add_field(name="USER", value=self.split_usernames(box_data[0][5]), inline=True) embed.add_field(name="ROOT", value=self.split_usernames(box_data[0][6]), inline=True) return embed return False async def send_pwn_notification(self, channel, username, action, box_name): channel = self.get_channel(int(channel)) self.log.info("Sending notification for {} on {} ({})".format(action, box_name, username)) await channel.send(">>> **{}** owned {} on {}!".format(username.capitalize(), action, box_name.upper()))
class B_FRAME(): def __init__(self, ip, port, username, password, debug=True): self.debug = debug self.log = Lumberjack("brute.log", self.debug) # True - x3270; False - s3270 self.emulator = WrappedEmulator(True) self.ip = ip self.port = port self.username = username self.password = password time.sleep(2) self.connect() self.login() if self.debug: self.print_screen() def connect(self): self.log.info("Connecting to {}:{}".format(self.ip, self.port)) self.emulator.connect("{}:{}".format(self.ip, self.port)) self.emulator.send_enter() if not self.emulator.is_connected(): self.log.error("Failed to connect") sys.exit(1) self.log.info("Connected successfully") def login(self): self.log.info("Loggin as " + self.username) self.emulator.move_to(0, 0) self.emulator.send_string(self.username) self.emulator.move_to(1, 1) self.emulator.send_string(self.password) self.emulator.send_enter() time.sleep(2) def get_screen_data(self): return self.emulator.exec_command(b'ASCII').data def print_screen(self): screen_data = self.get_screen_data() for line in screen_data: self.log.debug(line.decode()) def disconnect(self): self.log.info("Disconnecting...") self.emulator.exec_command(b'DISCONNECT') def create_html(self, filename): self.log.debug("Creating HTML file") full_filename = "./html/{}/{}.html".format(self.username, filename) if os.path.isfile(full_filename): os.remove(full_filename) self.emulator.exec_command( "PRINTTEXT(HTML, file, {})".format(full_filename).encode()) def test_command(self, command): if self.debug: self.log.debug("Testing command: " + command) self.emulator.move_to(10, 10) self.emulator.send_string(command) if self.debug: self.print_screen() self.emulator.send_enter() if self.debug: self.print_screen() def go_home(self): self.emulator.send_pf3() screen_data = self.get_screen_data() if b'SOME TEXT' not in screen_data[0]: self.log.warning("Filed to exit menu") self.disconnect() time.sleep(2) self.connect() time.sleep(2) self.login()
class ResolveDomains(): IP_FILENAME = "ips.txt" DOMAIN_IP_FILENAME = "domains_ips.csv" UNRESOLVED_FILENAME = "unresolved.txt" def __init__(self, input_file, output_path, verbose): self.input_file = input_file self.output_path = output_path self.log = Lumberjack(False, verbose) if not os.path.exists(self.output_path): self.log.error("Folder {} not found".format(self.output_path)) sys.exit(1) self.domains = { "resolved": {}, "unresolved": [] } self.log.info("Starting resolving domains...") def main(self): with open(self.input_file, "r") as domain_file: for domain in domain_file: self.check_domain(domain.strip()) self.log.debug(self.domains) self.create_files() def check_domain(self, domain): resolver = dns.resolver.Resolver() resolver.nameservers = ["4.2.2.1", "8.8.8.8"] try: ips = resolver.resolve(domain, "A") if ips: self.log.info("Resolved " + domain) self.domains["resolved"][domain] = [ips[0].to_text()] self.log.debug("%s: %s" % (domain, ips[0].to_text())) for index, ip in enumerate(ips): if index != 0: self.domains["resolved"][domain] += [ip.to_text()] self.log.debug("%s: %s" % (domain, ip.to_text())) # Remove duplicates self.domains["resolved"][domain] = list(set(self.domains["resolved"][domain])) else: self.domains["unresolved"] += [domain] self.log.warning("Could not resolve " + domain) except: self.domains["unresolved"] += [domain] self.log.warning("Could not resolve " + domain) def create_files(self): with open(os.path.join(self.output_path, self.IP_FILENAME), "w") as ip_file: all_ips = [] for domain, ips in self.domains["resolved"].items(): for ip in ips: if ip not in all_ips: all_ips += [ip] all_ips = sorted(all_ips, key=ipaddress.IPv4Address) for ip in all_ips: ip_file.write(ip + "\n") with open(os.path.join(self.output_path, self.DOMAIN_IP_FILENAME), "w") as domain_ip_file: for domain, ips in self.domains["resolved"].items(): domain_ip_file.write("{},{}\n".format(domain, "/".join(ips))) with open(os.path.join(self.output_path, self.UNRESOLVED_FILENAME), "w") as unresolved_file: for domain in self.domains["unresolved"]: unresolved_file.write(domain + "\n")
class FormatDomainsForScreenshots(): WEB_PORTS = list( map(str, [*range(8000, 8010), 8443, *range(8080, 8090), *range(8880, 8890) ])) def __init__(self, nmap_file, domain_file, output_file, verbose): self.nmap_file = nmap_file self.domain_file = domain_file self.output_file = output_file self.log = Lumberjack(False, verbose) self.log.info("Starting formating domains...") def main(self): all_domains = self.parse_nmap_xml() all_domains = list(set(all_domains)) with open(self.output_file, "w") as output_file: for domain in all_domains: output_file.write(domain + "\n") def parse_nmap_xml(self): all_domains = [] tree = ET.parse(self.nmap_file) root = tree.getroot() for host in root.findall("host"): ip = host.find("address").get('addr') domains = self.find_domain_by_ip(ip) if not domains: self.log.error("Couldn't find " + ip) domains = ip ports = host.find("ports").findall("port") self.log.info("Adding endpoints for " + ip) for port in ports: if port.find("state").get("state") == "open": port_id = port.get("portid") for domain in domains: if port_id == "80": all_domains.append("http://{}:80".format(domain)) elif port_id == "443": all_domains.append("https://{}:443".format(domain)) elif port_id in self.WEB_PORTS: all_domains.append("http://{}:{}".format( domain, port_id)) all_domains.append("https://{}:{}".format( domain, port_id)) return all_domains def find_domain_by_ip(self, ip): found_domains = [] with open(self.domain_file, "r") as domain_file: for line in domain_file: if ip in line.split(",")[1].split("\n")[0].split("/"): found_domains.append(line.split(",")[0]) return found_domains