#!/usr/bin/python3 import json import sys from enum import Enum DEBUG = False full_output = False hosts = [] # Describe the port fields class Port: port_number = 0 state = "" protocol = "" owner = "" service = "" sunRPCinfo = "" version = "" def __str__(self): return f"Port {self.port_number} - Version: {self.version}" def todict(self, full=False): if full: return { "port":self.port_number, "state":self.state, "protocol":self.protocol, "owner":self.owner, "service":self.service, "RPCInfo":self.sunRPCinfo, "version":self.version, } return { "port":self.port_number, "protocol":self.protocol, "service":self.service, "version":self.version, } # Class to represent all the host attributes class Host: ip = "" status = "" hostname = "" ports = [] # Array of Port() items def todict(self, full): return { "ip":self.ip, "hostname":self.hostname, "ports":[port.todict(full) for port in self.ports], "status":self.status } def open_gnmap(filename): with open(filename, "r") as gnmap: # split into lines and only take the ones that begin with "Host:" lines = gnmap.read().strip().split("\n") lines = [l for l in lines if not l.startswith("#") and l.startswith("Host:")] return lines # parse every single line def parse_line(line): global hosts # split line into different fields fields = line.split("\t") # parse the different fields out_host = parse_fields(fields) merge_hosts(out_host) def parse_host_field(field): host = Host() subfields = field.split(" (") host.ip = subfields[0] host.hostname =subfields[1].replace(")", "") if DEBUG: print(f"DEBUG: parse_host_field::ip={host.ip};hostname={host.hostname}") return host def parse_ports_field(host, field): portlist = field.split("/,") # loop through the ports and append them to the host for p in portlist: port = Port() portparts = p.split("/") port.port_number = portparts[0] port.state = portparts[1] port.protocol = portparts[2] port.owner = portparts[3] port.service = portparts[4] port.sunRPCinfo = portparts[5] port.version = portparts[6] host.ports.append(port) return host def parse_status_field(host, field): host.status = field return host # Jumptable for the different fields field_parser = { "Host": parse_host_field, "Ports": parse_ports_field, "Status": parse_status_field, } # if no parser for that type of field is implemented def not_implemented(host, field_type): return host def merge_hosts(host): global hosts for ih in hosts: if ih.ip == host.ip: ih.ports = host.ports break else: hosts.append(host) # parsed the different fields, each separeted by a space def parse_fields(fields): # create a Host instance host = Host() # loop each field that is present for field in fields: field_type, field_value = field.split(": ") if DEBUG: print(f"DEBUG: field_type={field_type}") if DEBUG: print(f"DEBUG: field_content={field_value}") # Host Field is the most important one if field_type == "Host": host = parse_host_field(field_value) else: host = field_parser.get(field_type, not_implemented)(host, field_value) return host def hosts_to_json(full_output): global hosts out = [] for host in hosts: out.append(host.todict(full_output)) return out # check usage if len(sys.argv) == 1: print(f"usage: {sys.argv[0]} <scan.gnmap>") exit(1) elif len(sys.argv) == 3 and sys.argv[2] == "--full": full_output = True # Open the gnmap file lines = open_gnmap(sys.argv[1]) # parse file for line in lines: parse_line(line) # dump as json print(json.dumps(hosts_to_json(full_output)))