def test_host_pretty_print(self): '''Host pretty print is used when generating templates for email''' sd_scan = ndr.NmapScan() sd_scan.parse_nmap_xml(load_nmap_xml_data(TEST_SD_SCAN_DATA)) host = sd_scan.find_by_ip("72.14.184.41") with open(OUTPUT_PRETTY_PRINT_SD_SCAN, 'r') as f: expected_sd_pp = f.read() self.assertEqual(expected_sd_pp, host.pretty_print_str())
def test_host_hash(self): sd_scan = ndr.NmapScan() sd_scan.scan_type = ndr.NmapScanTypes.SERVICE_DISCOVERY sd_scan.parse_nmap_xml(load_nmap_xml_data(TEST_SD_SCAN_DATA)) sd_yaml = sd_scan.to_yaml() sd_scan2 = ndr.NmapScan.from_yaml(sd_yaml) host1 = sd_scan.hosts.pop() host2 = sd_scan2.hosts.pop() self.assertEqual(host1, host2)
def test_yaml_serialization_deserialization(self): '''Tests that we can successfully serialize and deserialize the NMap scans''' sd_scan = ndr.NmapScan() sd_scan.scan_type = ndr.NmapScanTypes.SERVICE_DISCOVERY sd_scan.parse_nmap_xml(load_nmap_xml_data(TEST_SD_SCAN_DATA)) sd_yaml = sd_scan.to_yaml() sd_scan2 = ndr.NmapScan.from_yaml(sd_yaml) sd_scan2.scan_type = ndr.NmapScanTypes.SERVICE_DISCOVERY # Because we don't have a recursive __eq__ mechanism in place # yet, reserialize the YAML and see if it equals self.assertEqual(sd_yaml, sd_scan2.to_yaml())
def test_ipv6_link_local_scan_from_message(self): '''Tests reading link local scan, as this handles the case where there's no port or service information. This failed on the initial import into the DB so is a regression test in additional to a functionality test''' with open(TEST_LINK_LOCAL_PARSING, 'r') as f: scan_raw = f.read() msg = ndr.IngestMessage() msg.load_from_yaml(scan_raw) ipv6_ll_scan = ndr.NmapScan() ipv6_ll_scan.from_message(msg) self.assertEqual(len(ipv6_ll_scan.hosts), 2)
def test_port_detection(self): '''Ensures that port detection information is actually there''' # We have two types of scans with port information in them, let's test them. port_info_scan = ndr.NmapScan() port_info_scan.parse_nmap_xml( load_nmap_xml_data(TEST_V6_LINK_LOCAL_SCAN_DATA)) host = port_info_scan.find_by_mac('84:39:BE:64:3F:E5')[0] self.assertIsNotNone(host) self.assertIsInstance(host.get_open_port(22, ndr.PortProtocols.TCP), ndr.NmapPort) self.assertIsInstance(host.get_open_port(25, ndr.PortProtocols.TCP), ndr.NmapPort) self.assertIsNone(host.get_open_port(22, ndr.PortProtocols.UDP))
def run_scan(self, scan_type, options, target): '''Does a IPv4 network scan''' xml_logfile = tempfile.mkstemp() # Invoke NMAP self.config.logger.debug("Scanning Target: %s", target) self.config.logger.debug("Options: %s", options) # Build the full nmap command line nmap_cmd = ["nmap"] nmap_cmd += (options.split(" ")) # Build in XML output nmap_cmd += ["-oX", xml_logfile[1]] if target is not None: nmap_cmd += [target.compressed] self.config.logger.debug("NMap Command: %s", ' '.join(nmap_cmd)) nmap_proc = subprocess.run(args=nmap_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False) if nmap_proc.returncode != 0: raise ndr.NmapFailure(nmap_proc.returncode, str(nmap_proc.stderr, 'utf-8')) xml_output = None with open(xml_logfile[1], 'r') as nmap_file: xml_output = nmap_file.read() os.remove(xml_logfile[1]) # Build a scan object and return it nmap_scan = ndr.NmapScan(config=self.config) nmap_scan.parse_nmap_xml(xml_output) nmap_scan.scan_type = scan_type # Scan targets MUST be in CIDR form, so convert the target to a ipnetwork if target is not None: target_net = ipaddress.ip_network(target.compressed) nmap_scan.scan_target = target_net.compressed return nmap_scan
def test_ipv6_linklocal_parsing(self): '''Tests the results of the IPv6 link-local scan''' scan = ndr.NmapScan() scan.parse_nmap_xml(load_nmap_xml_data(TEST_V6_LINK_LOCAL_SCAN_DATA)) self.assertEqual(3, len(scan)) # Got three hosts, find a host by MAC address host_list = scan.find_by_mac('84:39:BE:64:3F:E5') # SHould only find one IP address self.assertEqual(len(host_list), 1) host = host_list[0] self.assertTrue(host.reason, ndr.NmapReasons.ND_RESPONSE) self.assertEqual(host.addr, ipaddress.ip_address("fe80::8639:beff:fe64:3fe5"))
def test_scandata_parsing(self): '''Tests parsing of the basic surface scan''' scan = ndr.NmapScan() scan.parse_nmap_xml(load_nmap_xml_data(TEST_SURFACE_SCAN_DATA)) self.assertEqual(5, len(scan)) # Got three hosts, find a host by MAC address host = scan.find_by_mac('84:39:BE:64:3F:E5')[0] # SHould only find one IP address self.assertNotEqual(None, host) self.assertTrue(host.addr, ipaddress.ip_address("192.168.2.100")) self.assertEqual(host.reason, ndr.NmapReasons.ARP_RESPONSE) # Confirm that we're not finding phantom hosts self.assertEqual(scan.find_by_mac('NOT_A_REAL_MAC'), None)
def test_service_discovery(self): '''Tests functioning of NmapService code''' sd_scan = ndr.NmapScan() sd_scan.parse_nmap_xml(load_nmap_xml_data(TEST_SD_SCAN_DATA)) host = sd_scan.find_by_ip("72.14.184.41") self.assertIsNotNone(host) # Test sercured SMTP port_obj = host.get_open_port(465, ndr.PortProtocols.TCP) self.assertIsInstance(port_obj, ndr.NmapPort) # Port 465 should be running postfix self.assertEqual(port_obj.service.name, "smtp") self.assertEqual(port_obj.service.product, "Postfix smtpd") self.assertEqual(port_obj.service.tunnel, "ssl") self.assertEqual(port_obj.service.method, ndr.NmapServiceMethods.PROBED) self.assertEqual(port_obj.service.confidence, 10) # Make sure other values are none self.assertIsNone(port_obj.service.rpcnum) # Confirm the CPE is there and what we expect self.assertEqual(port_obj.service.cpes[0], "cpe:/a:postfix:postfix") # Check the script element data on this port self.assertIsNone(port_obj.find_script_output_by_id("not-a-real-id")) script = port_obj.find_script_output_by_id("smtp-commands") self.assertIsNotNone(script) self.assertEqual( script.output, "mail.soylentnews.org, PIPELINING, SIZE 10240000, ETRN, AUTH PLAIN LOGIN, AUTH=PLAIN LOGIN, ENHANCEDSTATUSCODES, 8BITMIME, DSN, " ) # Now try getting some elements script = port_obj.find_script_output_by_id("ssl-cert") # Ugh, this horrid, see rant in nmap.py self.assertEqual(script.elements[1]['md5'], '66f70512e57e801e8c54f8956c808ddf')
def test_operating_system_detection(self): '''Determine via best guess of what operating system a device may or may not be running''' port_info_scan = ndr.NmapScan() port_info_scan.parse_nmap_xml(load_nmap_xml_data(TEST_SD_SCAN_DATA)) host = port_info_scan.find_by_ip("72.14.184.41") self.assertIsNotNone(host) self.assertIsNotNone(host.osmatches) self.assertEqual(len(host.osmatches), 1) self.assertEqual(host.osmatches[0].name, "Linux 3.11 - 4.1") self.assertEqual(host.osmatches[0].accuracy, 100) osclass = host.osmatches[0].osclasses[0] self.assertEqual(osclass.ostype, "general purpose") self.assertEqual(osclass.vendor, "Linux") self.assertEqual(osclass.cpes[0], "cpe:/o:linux:linux_kernel:3")
def create_from_message(cls, config, recorder, log_id, message, db_conn=None): '''Creates a NetworkScan object from the database''' storable_scan = ndr.NmapScan() storable_scan.from_message(message) net_scan = NetworkScan(config) net_scan.recorder = recorder net_scan.nmap_scan = storable_scan net_scan.message = message scan_json = json.dumps(storable_scan.to_dict()) net_scan.pg_id = config.database.run_procedure_fetchone( "network_scan.import_scan", [log_id, scan_json], existing_db_conn=db_conn)[0] return net_scan
def test_hostname_resolution(self): '''Tests the scanning of hostname and rDNS data''' hostname_scan = ndr.NmapScan() hostname_scan.parse_nmap_xml( load_nmap_xml_data(TEST_HOSTNAME_SCAN_DATA)) host = hostname_scan.hosts.pop() self.assertTrue(host.has_hostname("soylentnews.org")) self.assertTrue(host.has_hostname("li941-192.members.linode.com")) self.assertFalse(host.has_hostname("not-sn.org")) # Test hostname obj comparsion sn_hostname = ndr.NmapHostname("soylentnews.org", ndr.NmapHostnameTypes.USER) sn_hostname_ptr = ndr.NmapHostname( "soylentnews.org", ndr.NmapHostnameTypes.PTR) # shouldn't match li694_hostname = ndr.NmapHostname("li941-192.members.linode.com", ndr.NmapHostnameTypes.PTR) self.assertTrue(host.has_hostname(sn_hostname)) self.assertFalse(host.has_hostname(sn_hostname_ptr)) self.assertTrue(host.has_hostname(li694_hostname))