nmap-scan.py (7922B)
1 #! /usr/bin/env python3 2 """ 3 Usage: nmap-scan [options] <network> 4 5 Options: 6 --mongo-host MONGO_HOST Mongo Host(default: localhost) 7 --mongo-port MONGO_PORT Mongo Port(default: 3001) 8 --database DB Add records to mongo db 9 --collection COLLECTION Add records to mongo collection 10 -H, --hosts Print *nix hosts file format [default]. 11 -R, --raw Print raw collection database 12 -I, --insert Performs insert instead of in place update 13 -S, --snmp Make SNMP calls 14 -A, --ansible Make Ansible inventory 15 -D, --debug Print debug information 16 17 """ 18 from pysnmp.hlapi import * 19 import xml.etree.ElementTree as ET 20 from docopt import docopt 21 from pprint import pprint 22 from pymongo import MongoClient 23 import datetime 24 import os 25 import sys 26 27 from pprint import pprint 28 29 if os.name == 'posix' and sys.version_info[0] < 3: 30 import subprocess32 as subprocess 31 from subprocess32 import CalledProcessError 32 else: 33 import subprocess 34 35 out = "" 36 37 def debug(msg, obj): 38 if obj is not None: 39 print("DEBUG: {0} {1}\n".format(msg, pprint(obj))) 40 41 def run_nmap(net): 42 try: 43 out = subprocess.check_output(["nmap", "-oX", "-" , "-R", "-p", "22-443", "-sV" , net]) 44 except CalledProcessError: 45 print("Error in caller\n") 46 exit(1) 47 return out 48 49 def parsexml(f): 50 _host = {} 51 tree = ET.fromstring(f) 52 hosts = tree.findall('host') 53 for host in hosts: 54 if ET.iselement(host): 55 for addr in host.findall("address"): 56 _address_type = addr.get('addrtype') 57 if _address_type == 'ipv4': 58 _ipaddress = addr.get('addr') 59 _ipaskey = _ipaddress.replace(".", "-") 60 _macaddress = None 61 _vendor = None 62 # _host[_ipaskey] = [] 63 if _address_type == 'mac': 64 _macaddress = addr.get('addr') 65 _vendor = addr.get('vendor') 66 if args["--snmp"]: 67 if debug: 68 print("Run SNMPGET for {}".format(_ipaddress)) 69 varBind = snmpget(_ipaddress) 70 71 72 73 hostname = host.find("./hostnames/hostname").get('name') if ET.iselement(host.find("./hostnames/hostname")) else _ipaskey 74 # Per Host 75 _host[_ipaskey] = { '_id': _ipaskey, 'name': hostname, 'ip': _ipaddress, 'mac': _macaddress, 'vendor': _vendor} 76 _services = {} 77 for port in host.findall("./ports/port"): 78 if ET.iselement(port): 79 if port.find(".//state/[@state='open']") is not None: 80 #Per open Port 81 state = port.find(".//state/[@state='open']") 82 service = port.find('service') 83 84 if port.get("portid") == "443": 85 common_name = port.find("./script/[@id='ssl-cert']/table/[@key='subject']/elem/[@key='commonName']").text \ 86 if ET.iselement(port.find("./script/[@id='ssl-cert']/table/[@key='subject']/elem/[@key='commonName']")) else None 87 valid_after = port.find("./script/[@id='ssl-cert']/table/[@key='validity']/elem/[@key='notAfter']").text \ 88 if ET.iselement(port.find("./script/[@id='ssl-cert']/table/[@key='validity']/elem/[@key='notAfter']")) else None 89 orgname = port.find("./script/[@id='ssl-cert']/table/[@key='subject']/elem/[@key='organizationName']").text \ 90 if ET.iselement(port.find("./script/[@id='ssl-cert']/table/[@key='subject']/elem/[@key='organizationName']")) else None 91 _service = {'protocol' : port.get("protocol"), 92 'portid' : port.get("portid"), 93 'product' : service.get("product"), 94 'service_name' : service.get("name"), 95 'ssl_cn' : common_name, 96 'ssl_orgname' : orgname, 97 'ssl_valid' : valid_after,} 98 else: 99 _service = {'protocol' : port.get("protocol"), 100 'portid' : port.get("portid"), 101 'product' : service.get("product"), 102 'service_name' : service.get("name"),} 103 104 if port.get("portid") not in list(_services.values()): 105 _services.setdefault('services',[]).append(_service) 106 107 108 _host[_ipaskey].update(_services) 109 del _services 110 111 if args["--debug"]: 112 debug("_host Object", _host) 113 114 return _host 115 116 117 def snmpget(host): 118 119 cmdGen = cmdgen.CommandGenerator() 120 121 errorIndication, errorStatus, errorIndex, varBinds = next( 122 getCmd(SnmpEngine(), 123 CommunityData('public'), 124 UdpTransportTarget((host, 161)), 125 ContextData(), 126 ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))) 127 ) 128 129 # Check for errors and print out results 130 if errorIndication: 131 print(("Found ErrorI: {}".format(errI))) 132 return None 133 elif errorStatus: 134 errS = errorStatus 135 print(("Found ErrorS: {}".format(errorStatus))) 136 return None 137 else: 138 for v in varBinds: 139 if debug: 140 print(("Varbinds: {}".format(pprint(varBinds)))) 141 # print('%s = %s' % (name.prettyPrint(), val.prettyPrint())) 142 return varBinds 143 144 def print_hosts(collection): 145 for k,v in collection.items(): 146 print("%s\t%s" % (v['ip'],v['name'])) 147 148 149 def print_ansible(collection): 150 print("[hosts]") 151 for i in collection: 152 print("%s\tansible_ssh_user=%s\tansible_ssh_pass=%s" % (i, 'root', '#password#')) 153 154 def print_raw(collection): 155 pprint(collection) 156 157 def update_collection(collection, col): 158 timestamp = str(datetime.datetime.now()) 159 160 for i in collection: 161 collection[i].update({'updated': timestamp}) 162 query = { "_id" : i } 163 # query_one = { ip : i }, {{ ip : 1 } 164 if args['--insert']: 165 insert(collection[i], col) 166 else: 167 find_and_modify(query, collection[i], col) 168 169 170 def get_mongo_handle(client, database, collection): 171 """ 172 Get a collections handle in a database returns database and collection object 173 """ 174 db = getattr(client,database) 175 col = getattr(db,collection) 176 return (db, col) 177 178 def find_and_modify(query, record, col): 179 """ 180 Find a document in collection based on index and update 181 """ 182 retval = col.find_and_modify(query, update={'$set' : record}, upsert=True, new=True) 183 184 return retval 185 186 def find(query, col): 187 """ 188 Find a document in collection based on query 189 """ 190 retval = col.find(query).count() 191 192 if retval > 1: 193 return True 194 else: 195 return False 196 197 def find_one(query, col): 198 """ 199 Find a document in collection based on query 200 """ 201 retval = col.find_one(query) 202 if retval is not None: 203 return True 204 else: 205 return False 206 207 def insert(record, col): 208 """ 209 Find a document in collection based on query 210 """ 211 retval = col.insert(record, manipulate=True) 212 213 if retval is not None: 214 return True 215 else: 216 return False 217 218 219 if __name__ == "__main__": 220 args = docopt(__doc__) 221 network = args["<network>"] 222 output = run_nmap(network) 223 records = parsexml(output) 224 225 if args["--hosts"]: 226 print_hosts(records) 227 elif args["--ansible"]: 228 print_ansible(records) 229 elif args["--raw"]: 230 print_raw(records) 231 232 elif args["--mongo-host"]: 233 url = "mongodb://{host}:{port}/".format(host=args["--mongo-host"], port=args["--mongo-port"]) 234 client = MongoClient(url) 235 (db, col) = get_mongo_handle(client, args["--database"], args["--collection"]) 236 update_collection(records, col) 237 client.close() 238 else: 239 print("Printing host records\n") 240 print_hosts(records) 241 242 243