Skip to content

Commit 76a06ca

Browse files
committed
Merged script changes
2 parents a587d76 + 02faf50 commit 76a06ca

File tree

4 files changed

+95
-39
lines changed

4 files changed

+95
-39
lines changed

nmap3/exceptions.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323
import sys
2424
import re
2525

26-
__author__ = 'Wangolo Joel ([email protected])'
27-
__version__ = '1.4.7'
28-
__last_modification__ = '2029/06/11'
29-
26+
__author__ = 'Wangolo Joel ([email protected])'
27+
__version__ = '1.4.9'
28+
__last_modification__ = '2029/12/11'
3029

3130
class NmapNotInstalledError(Exception):
3231
"""Exception raised when nmap is not installed"""
@@ -41,4 +40,7 @@ class NmapXMLParserError(Exception):
4140
def __init__(self, message="Unable to parse xml output"):
4241
self.message = message
4342
super().__init__(message)
44-
43+
44+
class NmapExecutionError(Exception):
45+
"""Exception raised when en error occurred during nmap call"""
46+

nmap3/nmap3.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,13 @@
2929
import argparse
3030
from xml.etree import ElementTree as ET
3131
from xml.etree.ElementTree import ParseError
32-
from nmap3.exceptions import NmapNotInstalledError, NmapXMLParserError
3332
from nmap3.nmapparser import NmapCommandParser
3433
from nmap3.utils import get_nmap_path, user_is_root
35-
34+
from nmap3.exceptions import NmapNotInstalledError, NmapXMLParserError, NmapExecutionError
3635
import xml
3736

38-
__author__ = 'Wangolo Joel (info@nmapper.com)'
39-
__version__ = '1.4.7'
37+
__author__ = 'Wangolo Joel (inquiry@nmapper.com)'
38+
__version__ = '1.4.9'
4039
__last_modification__ = '2020/12/10'
4140
OS_TYPE = sys.platform
4241

@@ -59,13 +58,23 @@ def __init__(self, path=None):
5958
self.top_ports = dict()
6059
self.parser = NmapCommandParser(None)
6160
self.raw_ouput = None
61+
self.as_root = False
62+
63+
def require_root(self, required=True):
64+
"""
65+
Call this method to add "sudo" in front of nmap call
66+
"""
67+
self.as_root = required
6268

6369
def default_command(self):
6470
"""
6571
Returns the default nmap command
6672
that will be chained with all others
6773
eg nmap -oX -
6874
"""
75+
if self.as_root:
76+
return self.default_command_privileged()
77+
6978
return self.default_args.format(nmap=self.nmaptool, outarg="-oX")
7079

7180
def default_command_privileged(self):
@@ -240,19 +249,22 @@ def run_command(self, cmd):
240249
241250
@param: cmd--> the command we want run eg /usr/bin/nmap -oX - nmmapper.com --top-ports 10
242251
"""
243-
if(os.path.exists(self.nmaptool)):
244-
sub_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
252+
if (os.path.exists(self.nmaptool)):
253+
sub_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
245254
try:
246255
output, errs = sub_proc.communicate()
247256
except Exception as e:
248257
sub_proc.kill()
249-
raise(e)
258+
raise (e)
250259
else:
260+
if 0 != sub_proc.returncode:
261+
raise NmapExecutionError('Error during command: "' + ' '.join(cmd) + '"\n\n' + errs.decode('utf8'))
262+
251263
# Response is bytes so decode the output and return
252264
return output.decode('utf8').strip()
253265
else:
254266
raise NmapNotInstalledError()
255-
267+
256268
def get_xml_et(self, command_output):
257269
"""
258270
@ return xml ET
@@ -318,14 +330,15 @@ def tpl(i):
318330

319331
return xml_root
320332

321-
322333
def nmap_fin_scan(self, target, args=None):
323334
"""
324335
Perform scan using nmap's fin scan
325336
326337
@cmd nmap -sF 192.168.178.1
327338
328339
"""
340+
self.require_root()
341+
329342
xml_root = self.scan_command(self.fin_scan, target=target, args=args)
330343
results = self.parser.filter_top_ports(xml_root)
331344
return results
@@ -337,8 +350,8 @@ def nmap_syn_scan(self, target, args=None):
337350
338351
@cmd nmap -sS 192.168.178.1
339352
"""
353+
self.require_root()
340354
xml_root = self.scan_command(self.sync_scan, target=target, args=args)
341-
# Use the top_port_parser
342355
results = self.parser.filter_top_ports(xml_root)
343356
return results
344357

@@ -360,6 +373,8 @@ def nmap_udp_scan(self, target, args=None):
360373
361374
@cmd nmap -sU 192.168.178.1
362375
"""
376+
self.require_root()
377+
363378
if(args):
364379
assert(isinstance(args, str)), "Expected string got {0} instead".format(type(args))
365380
xml_root = self.scan_command(self.udp_scan, target=target, args=args)
@@ -476,12 +491,6 @@ def nmap_disable_dns(self, target, args=None):
476491
results = self.parser.filter_top_ports(xml_root)
477492
return results
478493

479-
class NmapScripts(Nmap):
480-
"""
481-
This will be responsible for the nmap extra scriptin engine
482-
"""
483-
pass
484-
485494
if __name__=="__main__":
486495
parser = argparse.ArgumentParser(prog="Python3 nmap")
487496
parser.add_argument('-d', '--d', help='Help', required=True)

nmap3/nmapparser.py

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,12 @@ def filter_top_ports(self, xmlroot):
101101

102102
port_result_dict["stats"]=stats
103103
port_result_dict["runtime"]=self.parse_runtime(xmlroot)
104-
port_result_dict["totals"]=self.total_hosts(xmlroot)
105104

106105
except Exception as e:
107106
raise(e)
108107
else:
109108
return port_result_dict
110-
109+
111110
def os_identifier_parser(self, xmlroot):
112111
"""
113112
Parser for identified os
@@ -179,7 +178,11 @@ def parse_ports(self, xml_hosts):
179178
cpe_list = []
180179
cpe_list.append({"cpe": cp.text})
181180
open_ports["cpe"] = cpe_list
181+
182+
# Script
183+
open_ports["scripts"]=self.parse_scripts(port.findall('script')) if port.findall('script') is not None else []
182184
open_ports_list.append(open_ports)
185+
183186
return open_ports_list
184187

185188
def parse_runtime(self, xml):
@@ -192,16 +195,7 @@ def parse_runtime(self, xml):
192195
if runstats is not None:
193196
if runstats.find("finished") is not None:
194197
return runstats.find("finished").attrib
195-
196-
def total_hosts(self, xml):
197-
"""
198-
Parse parts from xml
199-
"""
200-
hosts = xml.find("runstats/hosts")
201-
if hosts is not None:
202-
return hosts.attrib
203-
return {}
204-
198+
205199
def parse_mac_address(self, xml):
206200
"""
207201
Parse parts from xml
@@ -211,8 +205,7 @@ def parse_mac_address(self, xml):
211205
for addr in addresses:
212206
if(addr.attrib.get("addrtype") == "mac"):
213207
return addr.attrib
214-
return {}
215-
208+
216209
def parse_hostnames(self, host):
217210
"""
218211
Parse parts from xml
@@ -231,4 +224,56 @@ def get_hostname_state(self, xml):
231224
state = xml.find("status")
232225
if(state is not None):
233226
return state.attrib
234-
return {}
227+
228+
def parse_scripts(self, scripts_xml):
229+
scripts = []
230+
231+
for script_xml in scripts_xml:
232+
script_name = script_xml.attrib.get('id')
233+
raw_output = script_xml.attrib.get('output')
234+
235+
data = self.convert_xml_elements(script_xml)
236+
if script_xml.findall('table') is not None:
237+
tables = script_xml.findall('table')
238+
child_data = self.convert_xml_tables(tables)
239+
for k in child_data:
240+
if {} != k:
241+
data[k] = child_data[k]
242+
243+
scripts.append({
244+
'name': script_name,
245+
'raw': raw_output,
246+
'data': data
247+
})
248+
249+
return scripts
250+
251+
def convert_xml_tables(self, xml_tables):
252+
data = {}
253+
for xml_table in xml_tables:
254+
key = xml_table.attrib.get('key')
255+
child_data = self.convert_xml_elements(xml_table)
256+
if key is None:
257+
if {} != child_data:
258+
a = data.get('children', [])
259+
data['children'] = a + [child_data]
260+
else:
261+
if xml_table.findall('table') is not None:
262+
data[key] = self.convert_xml_tables(xml_table.findall('table'))
263+
if {} != child_data:
264+
a = data.get(key, {})
265+
b = a.get('children', [])
266+
a['children'] = b + [child_data]
267+
268+
return data
269+
270+
def convert_xml_elements(self, xml_obj):
271+
elements = {}
272+
elem_counter = 0
273+
for elem in xml_obj.findall('elem'):
274+
if None == elem.attrib.get('key'):
275+
elements[elem_counter] = elem.text
276+
else:
277+
elements[elem.attrib.get('key')] = elem.text
278+
elem_counter += 1
279+
return elements

nmap3/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
import re
2525
import os
2626

27-
__author__ = 'Wangolo Joel (info@nmapper.com)'
28-
__version__ = '0.1.1'
29-
__last_modification__ = '2019/11/22'
27+
__author__ = 'Wangolo Joel (inquiry@nmapper.com)'
28+
__version__ = '1.4.9'
29+
__last_modification__ = '2019/12/11'
3030

3131

3232
def get_nmap_path():

0 commit comments

Comments
 (0)