def get_vnc_screenshots(target, scan_id, proctimeout): scan_dir = utils.get_scan_dir(scan_id) output_file = os.path.join(scan_dir, f"vncsnapshot.{scan_id}.jpg") logger.info(f"Attempting to take VNC screenshot for {target}") vncsnapshot_args = [ "xvfb-run", "vncsnapshot", "-quality", "50", target, output_file, ] process = subprocess.Popen(vncsnapshot_args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # nosec try: process.communicate(timeout=proctimeout) except subprocess.TimeoutExpired: logger.warning(f"TIMEOUT: Killing vncsnapshot against {target}") process.kill() if not is_valid_image(output_file): return {} logger.info(f"VNC screenshot acquired for {target} on port 5900") return { "host": target, "port": 5900, "service": "VNC", "data": base64_file(output_file), }
def get_web_screenshots(target, scan_id, proctimeout): scan_dir = utils.get_scan_dir(scan_id) xml_file = os.path.join(scan_dir, f"nmap.{scan_id}.xml") output_dir = os.path.join(scan_dir, f"aquatone.{scan_id}") logger.info(f"Attempting to take screenshots for {target}") aquatoneArgs = [ "aquatone", "-nmap", "-scan-timeout", "2500", "-out", output_dir ] with open(xml_file, "r") as f: process = subprocess.Popen(aquatoneArgs, stdin=f, stdout=subprocess.DEVNULL) # nosec try: process.communicate(timeout=proctimeout) if process.returncode == 0: time.sleep( 0.5 ) # a small sleep to make sure all file handles are closed so that the agent can read them except subprocess.TimeoutExpired: logger.warning(f"TIMEOUT: Killing aquatone against {target}") process.kill() return parse_aquatone_session(output_dir)
def get_vnc_screenshots(target, scan_id, proctimeout): scan_dir = utils.get_scan_dir(scan_id) outFile = os.path.join(scan_dir, f"vncsnapshot.{scan_id}.jpg") logger.info(f"Attempting to take VNC screenshot for {target}") process = subprocess.Popen( ["xvfb-run", "vncsnapshot", "-quality", "50", target, outFile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) # nosec try: process.communicate(timeout=proctimeout) if process.returncode == 0: return True except Exception: try: logger.warning(f"TIMEOUT: Killing vncsnapshot against {target}") process.kill() return False except Exception: pass return False
def command_builder(scan_id, agentConfig, target): outFiles = os.path.join(utils.get_scan_dir(scan_id), f"nmap.{scan_id}") servicepath = utils.get_services_path() command = [ "nmap", "--privileged", "-oA", outFiles, "--servicedb", servicepath ] commandDict = { "versionDetection": "-sV", "osDetection": "-O", "osScanLimit": "--osscan-limit", "noPing": "-Pn", "onlyOpens": "--open", "udpScan": "-sUS", "enableScripts": "--script={scripts}", "scriptTimeout": "--script-timeout={scriptTimeout}", "hostTimeout": "--host-timeout={hostTimeout}", } for k, v in agentConfig.items(): if agentConfig[k] and k in commandDict: command.append(commandDict[k].format(**agentConfig)) if ipaddress.ip_network(target).version == 6: command.append("-6") command.append(target) return command
def get_web_screenshots(target, scan_id, xml_data, proctimeout): scan_dir = utils.get_scan_dir(scan_id) outFiles = os.path.join(scan_dir, f"aquatone.{scan_id}") output = [] logger.info(f"Attempting to take screenshots for {target}") p1 = subprocess.Popen(["echo", xml_data], stdout=subprocess.PIPE) # nosec aquatoneArgs = [ "aquatone", "-nmap", "-scan-timeout", "2500", "-out", outFiles ] process = subprocess.Popen(aquatoneArgs, stdin=p1.stdout, stdout=subprocess.DEVNULL) # nosec p1.stdout.close() try: process.communicate(timeout=proctimeout) if process.returncode == 0: time.sleep( 0.5 ) # a small sleep to make sure all file handles are closed so that the agent can read them except subprocess.TimeoutExpired: logger.warning(f"TIMEOUT: Killing aquatone against {target}") process.kill() session_path = os.path.join(outFiles, "aquatone_session.json") if not os.path.isfile(session_path): return output with open(session_path) as f: session = json.load(f) if session["stats"]["screenshotSuccessful"] > 0: logger.info( f"{target} - Success: {session['stats']['screenshotSuccessful']}, Fail: {session['stats']['screenshotFailed']}" ) for k, page in session["pages"].items(): fqScreenshotPath = os.path.join(outFiles, page["screenshotPath"]) if page["hasScreenshot"] and os.path.isfile(fqScreenshotPath): urlp = urlparse(page["url"]) if not urlp.port and urlp.scheme == "http": port = 80 elif not urlp.port and urlp.scheme == "https": port = 443 else: port = urlp.port logger.info( f"{urlp.scheme.upper()} screenshot acquired for {page['hostname']} on port {port}" ) output.append({ "host": page["hostname"], "port": port, "service": urlp.scheme.upper(), "data": base64_image(fqScreenshotPath), }) return output
def scan(target_data, config): if not utils.validate_target(target_data["target"], config): return False target = target_data["target"] scan_id = target_data["scan_id"] agentConfig = target_data["agent_config"] command = command_builder(scan_id, agentConfig, target) scan_dir = utils.get_scan_dir(scan_id) result = ScanResult(target_data, config) try: subprocess.run( command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=int(agentConfig["scanTimeout"]), ) # nosec except subprocess.TimeoutExpired: result.add_item("timed_out", True) logger.warning(f"TIMEOUT: Nmap against {target} ({scan_id})") return result logger.info(f"Nmap {target} ({scan_id}) complete") for ext in "nmap", "gnmap", "xml": path = os.path.join(scan_dir, f"nmap.{scan_id}.{ext}") try: with open(path, "r") as f: result.add_item(ext + "_data", f.read()) except Exception: logger.warning(f"Couldn't read {path}") return False try: nmap_report = NmapParser.parse(result.result["xml_data"]) except NmapParserException: logger.warning(f"Couldn't parse nmap.{scan_id}.xml") return False if nmap_report.hosts_total < 1: logger.warning(f"No hosts found in nmap.{scan_id}.xml") return False elif nmap_report.hosts_total > 1: logger.warning(f"Too many hosts found in nmap.{scan_id}.xml") return False elif nmap_report.hosts_down == 1: # host is down result.is_up(False) return result elif nmap_report.hosts_up == 1 and len(nmap_report.hosts) == 0: # host is up but no reportable ports were found result.is_up(True) result.add_item("port_count", 0) return result else: # host is up and reportable ports were found result.is_up(nmap_report.hosts[0].is_up()) result.add_item("port_count", len(nmap_report.hosts[0].get_ports())) if agentConfig["webScreenshots"] and shutil.which("aquatone") is not None: screens = screenshots.get_web_screenshots( target, scan_id, result.result["xml_data"], agentConfig["webScreenshotTimeout"], ) for item in screens: result.add_screenshot(item) if (agentConfig["vncScreenshots"] and "5900/tcp" in result.result["nmap_data"] and screenshots.get_vnc_screenshots( target, scan_id, agentConfig["vncScreenshotTimeout"])): screenshotPath = os.path.join(scan_dir, f"vncsnapshot.{scan_id}.jpg") if os.path.isfile(screenshotPath): result.add_screenshot({ "host": target, "port": 5900, "service": "VNC", "data": screenshots.base64_image(screenshotPath), }) logger.info(f"VNC screenshot acquired for {result.result['ip']}") # submit result return result
def scan(target_data, config): if not utils.validate_target(target_data["target"], config): return False target = target_data["target"] scan_id = target_data["scan_id"] agentConfig = target_data["agent_config"] command = command_builder(scan_id, agentConfig, target) scan_dir = utils.get_scan_dir(scan_id) result = ScanResult(target_data, config) try: subprocess.run( command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, timeout=int(agentConfig["scanTimeout"]), ) # nosec except subprocess.TimeoutExpired: add_breadcrumb(level="warn", message="Nmap scan timed out") result.add_item("timed_out", True) logger.warning(f"TIMEOUT: Nmap against {target} ({scan_id})") return result logger.info(f"Nmap {target} ({scan_id}) complete") for ext in "nmap", "gnmap", "xml": path = os.path.join(scan_dir, f"nmap.{scan_id}.{ext}") try: with open(path, "r") as f: result.add_item(ext + "_data", f.read()) except Exception: logger.warning(f"Couldn't read {path}") return False try: nmap_report = NmapParser.parse(result.result["xml_data"]) except NmapParserException: logger.warning(f"Couldn't parse nmap.{scan_id}.xml") return False if nmap_report.hosts_total < 1: logger.warning(f"No hosts found in nmap.{scan_id}.xml") return False elif nmap_report.hosts_total > 1: logger.warning(f"Too many hosts found in nmap.{scan_id}.xml") return False elif nmap_report.hosts_down == 1: # host is down result.is_up(False) return result elif nmap_report.hosts_up == 1 and len(nmap_report.hosts) == 0: # host is up but no reportable ports were found result.is_up(True) result.add_item("port_count", 0) return result else: # host is up and reportable ports were found result.is_up(nmap_report.hosts[0].is_up()) result.add_item("port_count", len(nmap_report.hosts[0].get_ports())) if agentConfig["webScreenshots"]: screens = screenshots.get_web_screenshots( target, scan_id, agentConfig["webScreenshotTimeout"]) for item in screens: result.add_screenshot(item) if agentConfig["vncScreenshots"] and "5900/tcp" in result.result[ "nmap_data"]: vnc_screenshot = screenshots.get_vnc_screenshots( target, scan_id, agentConfig["vncScreenshotTimeout"]) if vnc_screenshot: result.add_screenshot(vnc_screenshot) # submit result return result