nmap-to-json.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #!/usr/bin/python3
  2. import json
  3. import sys
  4. from enum import Enum
  5. DEBUG = False
  6. full_output = False
  7. hosts = []
  8. # Describe the port fields
  9. class Port:
  10. def __init__(self):
  11. self.port_number = 0
  12. self.state = ""
  13. self.protocol = ""
  14. self.owner = ""
  15. self.service = ""
  16. self.sunRPCinfo = ""
  17. self.version = ""
  18. def __str__(self):
  19. return f"Port {self.port_number} - Version: {self.version}"
  20. def todict(self, full=False):
  21. if full:
  22. return {
  23. "port":self.port_number,
  24. "state":self.state,
  25. "protocol":self.protocol,
  26. "owner":self.owner,
  27. "service":self.service,
  28. "RPCInfo":self.sunRPCinfo,
  29. "version":self.version,
  30. }
  31. return {
  32. "port":self.port_number,
  33. "protocol":self.protocol,
  34. "service":self.service,
  35. "version":self.version,
  36. "state":self.state,
  37. }
  38. # Class to represent all the host attributes
  39. class Host:
  40. def __init__(self):
  41. self.ip = ""
  42. self.status = ""
  43. self.hostname = ""
  44. self.ports = [] # Array of Port() items
  45. def merge_ports(self, host):
  46. local_ports = [p.port_number for p in self.ports]
  47. for p in host.ports:
  48. if p.port_number not in local_ports:
  49. self.ports.append(p)
  50. def todict(self, full):
  51. return {
  52. "ip":self.ip,
  53. "hostname":self.hostname,
  54. "ports":[port.todict(full) for port in self.ports],
  55. "status":self.status
  56. }
  57. def open_gnmap(filename):
  58. with open(filename, "r") as gnmap:
  59. # split into lines and only take the ones that begin with "Host:"
  60. lines = gnmap.read().strip().split("\n")
  61. lines = [l for l in lines if not l.startswith("#") and l.startswith("Host:")]
  62. return lines
  63. # parse every single line
  64. def parse_line(line):
  65. global hosts
  66. # Create new Instance
  67. host = Host()
  68. # Split line into different fields
  69. fields = line.split("\t")
  70. # parse the different fields
  71. parse_fields(host, fields)
  72. # merge known hosts
  73. merge_hosts(host)
  74. # delete instance
  75. del host
  76. def parse_host_field(host, field):
  77. subfields = field.split(" (")
  78. host.ip = subfields[0]
  79. host.hostname =subfields[1].replace(")", "")
  80. if DEBUG: print(f"DEBUG: parse_host_field::ip={host.ip};hostname={host.hostname}")
  81. def parse_ports_field(host, field):
  82. portlist = [s.strip() for s in field.split("/,")]
  83. # loop through the ports and append them to the host
  84. for p in portlist:
  85. port = Port()
  86. portparts = p.split("/")
  87. port.port_number = portparts[0]
  88. port.state = portparts[1]
  89. port.protocol = portparts[2]
  90. port.owner = portparts[3]
  91. port.service = portparts[4]
  92. port.sunRPCinfo = portparts[5]
  93. port.version = portparts[6]
  94. host.ports.append(port)
  95. del port
  96. def parse_status_field(host, field):
  97. host.status = field
  98. return host
  99. # Jumptable for the different fields
  100. field_parser = {
  101. "Ports": parse_ports_field,
  102. "Status": parse_status_field,
  103. }
  104. # if no parser for that type of field is implemented
  105. def not_implemented(host, field_type):
  106. return host
  107. def merge_hosts(host):
  108. global hosts
  109. for ih in hosts:
  110. # hosts already present in list
  111. if ih.ip == host.ip:
  112. ih.merge_ports(host)
  113. break
  114. else:
  115. hosts.append(host)
  116. # parsed the different fields, each separeted by a space
  117. def parse_fields(host, fields):
  118. # create a Host instance
  119. # loop each field that is present
  120. for field in fields:
  121. field_parts = field.split(": ")
  122. field_type = field_parts[0]
  123. field_value = "".join(field_parts[1:])
  124. if DEBUG: print(f"DEBUG: field_type={field_type}")
  125. if DEBUG: print(f"DEBUG: field_content={field_value}")
  126. # Host Field is the most important one
  127. if field_type == "Host":
  128. parse_host_field(host, field_value)
  129. else:
  130. field_parser.get(field_type, not_implemented)(host, field_value)
  131. return host
  132. def hosts_to_json(full_output):
  133. global hosts
  134. out = []
  135. for host in hosts:
  136. out.append(host.todict(full_output))
  137. return out
  138. # check usage
  139. if len(sys.argv) == 1:
  140. print(f"usage: {sys.argv[0]} <scan.gnmap>")
  141. exit(1)
  142. elif len(sys.argv) == 3 and sys.argv[2] == "--full":
  143. full_output = True
  144. # Open the gnmap file
  145. lines = open_gnmap(sys.argv[1])
  146. # parse file
  147. for line in lines:
  148. parse_line(line)
  149. # dump as json
  150. print(json.dumps(hosts_to_json(full_output)))