1+ import io
2+ import os
3+ import re
4+ import csv
5+ import sys
6+ import shlex
7+ import subprocess
8+
9+ from xml .etree import ElementTree
10+ from multiprocessing import Process
11+
12+ import lib .jsonize
13+ import lib .errors
14+ import lib .output
15+ import lib .settings
16+
17+
18+ def write_xml_data (host , output ):
19+ if not os .path .exists (lib .settings .NMAP_XML_OUTPUT_BACKUP ):
20+ os .makedirs (lib .settings .NMAP_XML_OUTPUT_BACKUP )
21+ file_path = "{}/{}_{}.xml" .format (
22+ lib .settings .NMAP_XML_OUTPUT_BACKUP , str (host ), lib .jsonize .random_file_name (length = 10 )
23+ )
24+ with open (file_path , 'a+' ) as results :
25+ results .write (output )
26+ return file_path
27+
28+
29+ def find_nmap (search_paths ):
30+ for path in search_paths :
31+ try :
32+ _ = subprocess .Popen ([path , '-V' ], bufsize = 10000 , stdout = subprocess .PIPE , close_fds = True )
33+ except OSError :
34+ pass
35+ else :
36+ return path
37+ raise lib .errors .NmapNotFoundException
38+
39+
40+ def do_scan (host , nmap_path , ports = None , arguments = None ):
41+ if arguments is None :
42+ arguments = "-sV"
43+ arguments_list = shlex .split (arguments )
44+ launch_arguments = [
45+ nmap_path , '-oX' , '-' , host ,
46+ '-p ' + ports if ports is not None else "" ,
47+ ] + arguments_list
48+ lib .output .info ("launching nmap scan against {} ({})" .format (host , " " .join (launch_arguments )))
49+ process = subprocess .Popen (
50+ launch_arguments , bufsize = 10000 , stdin = subprocess .PIPE ,
51+ stdout = subprocess .PIPE , stderr = subprocess .PIPE
52+ )
53+ output , error = process .communicate ()
54+ output_data = bytes .decode (output )
55+ nmap_error = bytes .decode (error )
56+ nmap_error_tracestack = []
57+ nmap_warn_tracestack = []
58+ if len (nmap_error ) > 0 :
59+ for line in nmap_error .split (os .linesep ):
60+ if len (line ) != 0 :
61+ if lib .settings .NMAP_ERROR_REGEX_WARNING .search (line ) is not None :
62+ nmap_warn_tracestack .append (line + os .linesep )
63+ else :
64+ nmap_error_tracestack .append (line + os .linesep )
65+ path = write_xml_data (host , output_data )
66+ lib .output .misc_info ("a copy of the output has been saved to: {}" .format (path ))
67+ return output_data , "" .join (nmap_warn_tracestack ), "" .join (nmap_error_tracestack )
68+
69+
70+ def parse_xml_output (output , warnings , error ):
71+ results = {}
72+ try :
73+ root = ElementTree .fromstring (output )
74+ except Exception :
75+ if len (error ) != 0 :
76+ raise lib .errors .NmapScannerError (error )
77+ else :
78+ raise lib .errors .NmapScannerError (output )
79+ results ['nmap_scan' ] = {
80+ 'full_command_line' : root .get ('args' ),
81+ 'scan_information' : {},
82+ 'scan_stats' : {
83+ 'time_string' : root .find ('runstats/finished' ).get ('timestr' ),
84+ 'elapsed' : root .find ('runstats/finished' ).get ('elapsed' ),
85+ 'hosts_up' : root .find ('runstats/hosts' ).get ('up' ),
86+ 'down_hosts' : root .find ('runstats/hosts' ).get ('down' ),
87+ 'total_hosts_scanned' : root .find ('runstats/hosts' ).get ('total' )
88+ }
89+ }
90+ if len (error ) != 0 :
91+ results ['nmap_scan' ]['scan_information' ]['errors' ] = error
92+ if len (warnings ) != 0 :
93+ results ['nmap_scan' ]['scan_information' ]['warnings' ] = warnings
94+ for info in root .findall ('scaninfo' ):
95+ results ['nmap_scan' ]['scan_information' ][info .get ('protocol' )] = {
96+ 'method' : info .get ('type' ),
97+ 'services' : info .get ('services' )
98+ }
99+ for attempted_host in root .findall ('host' ):
100+ host = None
101+ addresses = {}
102+ vendors = {}
103+ for address in attempted_host .findall ("address" ):
104+ address_type = address .get ('addrtype' )
105+ addresses [address_type ] = address .get ('addr' )
106+ if address_type == "ipv4" :
107+ host = addresses [address_type ]
108+ elif address_type == "mac" and address .get ('vendor' ) is not None :
109+ vendors [addresses [address_type ]] = address .get ('vendor' )
110+ if host is None :
111+ host = attempted_host .find ('address' ).get ('addr' )
112+ hostnames = []
113+ if len (attempted_host .findall ('hostnames/hostname' )) != 0 :
114+ for current_hostnames in attempted_host .findall ('hostnames/hostname' ):
115+ hostnames .append ({
116+ 'hostname' : current_hostnames .get ('name' ),
117+ 'host_type' : current_hostnames .get ('type' )
118+ })
119+ else :
120+ hostnames .append ({
121+ 'hostname' : None ,
122+ 'host_type' : None
123+ })
124+
125+ results ['nmap_scan' ][host ] = {}
126+ results ['nmap_scan' ][host ]['hostnames' ] = hostnames
127+ results ['nmap_scan' ][host ]['addresses' ] = addresses
128+ results ['nmap_scan' ][host ]['vendors' ] = vendors
129+
130+ print results ;exit (1 )
131+
132+ for status in attempted_host .findall ('status' ):
133+ results ['nmap_scan' ][attempted_host ]['status' ] = {
134+ 'state' : status .get ('state' ),
135+ 'reason' : status .get ('reason' )
136+ }
137+ for uptime in attempted_host .findall ('uptime' ):
138+ results ['nmap_scan' ][attempted_host ]['uptime' ] = {
139+ 'seconds' : uptime .get ('seconds' ),
140+ 'lastboot' : uptime .get ('lastboot' )
141+ }
142+ for discovered_port in attempted_host .findall ('ports/port' ):
143+ protocol = discovered_port .get ('protocol' )
144+ port_number = discovered_port .get ('portid' )
145+ port_state = discovered_port .find ('state' ).get ('reason' )
146+
147+ # damn I didn't even know you could do this!
148+ for discovered_name in discovered_port .findall ('service' ):
149+ name = discovered_name .get ('name' )
150+ if discovered_name .get ('product' ):
151+ discovered_product = discovered_name .get ('product' )
152+ if discovered_name .get ('version' ):
153+ discovered_version = discovered_name .get ('version' )
154+ if discovered_name .get ('extrainfo' ):
155+ extra_information = discovered_name .get ('extrainfo' )
156+ print results
0 commit comments