def command_builder(scan_id, agentConfig, target): outFiles = utils.get_data_dir(scan_id) + f"/nmap.{scan_id}" command = [ "nmap", "--privileged", "-oA", outFiles, "--servicedb", "./tmp/natlas-services" ] 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)) command.append(target) return command
def get_vnc_screenshots(target, scan_id, proctimeout): if "DISPLAY" not in os.environ: return False data_dir = utils.get_data_dir(scan_id) outFile = f"{data_dir}/vncsnapshot.{scan_id}.jpg" logger.info("Attempting to take VNC screenshot for %s" % target) process = subprocess.Popen( ["xvfb-run", "vncsnapshot", "-quality", "50", target, outFile], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # nosec try: out, err = process.communicate(timeout=proctimeout) if process.returncode is 0: return True except Exception: try: logger.warning("TIMEOUT: Killing vncsnapshot against %s" % target) process.kill() return False except Exception: pass return False
def get_web_screenshots(target, scan_id, services, proctimeout): data_dir = utils.get_data_dir(scan_id) outFiles = f"{data_dir}/aquatone.{scan_id}" inputstring = "" for service in services: inputstring += service + "://" + target + "\n" if inputstring: inputstring = inputstring[: -1] # trim trailing newline because otherwise chrome spits garbage into localhost for some reason logger.info("Attempting to take %s screenshot(s) for %s" % (', '.join(services).upper(), target)) p1 = subprocess.Popen(["echo", inputstring], stdout=subprocess.PIPE) # nosec process = subprocess.Popen( ["aquatone", "-scan-timeout", "2500", "-out", outFiles], stdin=p1.stdout, stdout=subprocess.DEVNULL) # nosec p1.stdout.close() try: out, err = process.communicate(timeout=proctimeout) if process.returncode is 0: time.sleep( 0.5 ) # a small sleep to make sure all file handles are closed so that the agent can read them return True except subprocess.TimeoutExpired: logger.warning("TIMEOUT: Killing aquatone against %s" % target) process.kill() return False
def get_web_screenshots(target, scan_id, xml_data, proctimeout): data_dir = utils.get_data_dir(scan_id) outFiles = f"{data_dir}/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: out, err = 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) data_dir = utils.get_data_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("TIMEOUT: Nmap against %s (%s)" % (target, scan_id)) return result logger.info("Nmap %s (%s) complete" % (target, scan_id)) for ext in 'nmap', 'gnmap', 'xml': path = f"{data_dir}/nmap.{scan_id}.{ext}" try: result.add_item(ext + "_data", open(path).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 shutil.which( "vncsnapshot") is not None: if "5900/tcp" in result.result['nmap_data']: if screenshots.get_vnc_screenshots( target, scan_id, agentConfig["vncScreenshotTimeout"]) is True: screenshotPath = f"{data_dir}/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("VNC screenshot acquired for %s" % 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) data_dir = utils.get_data_dir(scan_id) result = ScanResult(target_data, config) try: process = 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("TIMEOUT: Nmap against %s (%s)" % (target, scan_id)) return result logger.info("Nmap %s (%s) complete" % (target, scan_id)) for ext in 'nmap', 'gnmap', 'xml': path = f"{data_dir}/nmap.{scan_id}.{ext}" try: result.add_item(ext + "_data", open(path).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: targetServices = [] if "80/tcp" in result.result['nmap_data']: targetServices.append("http") if "443/tcp" in result.result['nmap_data']: targetServices.append("https") if len(targetServices) > 0: screenshots.get_web_screenshots( target, scan_id, targetServices, agentConfig["webScreenshotTimeout"]) serviceMapping = {"http": 80, "https": 443} for service in targetServices: screenshotPath = f"{data_dir}/aquatone.{scan_id}/screenshots/{service}__{target.replace('.','_')}.png" # "data/aquatone." + scan_id + "/screenshots/" + service + "__" + target.replace('.', '_') + ".png" if not os.path.isfile(screenshotPath): continue result.add_screenshot({ "host": target, "port": serviceMapping[service], "service": service.upper(), "data": str(base64.b64encode(open(screenshotPath, 'rb').read()))[2:-1] }) logger.info("%s screenshot acquired for %s" % (service.upper(), target)) if agentConfig["vncScreenshots"] and shutil.which( "vncsnapshot") is not None: if "5900/tcp" in result.result['nmap_data']: if screenshots.get_vnc_screenshots( target, scan_id, agentConfig["vncScreenshotTimeout"]) is True: screenshotPath = f"{data_dir}/vncsnapshot.{scan_id}.jpg" if os.path.isfile(screenshotPath): result.add_screenshot({ "host": target, "port": 5900, "service": "VNC", "data": str(base64.b64encode( open(screenshotPath, 'rb').read()))[2:-1] }) logger.info("VNC screenshot acquired for %s" % result.result['ip']) # submit result return result