#!/usr/bin/env python #Copyright (C) 2009-2010 : # Gabes Jean, naparuba@gmail.com # Gerhard Lausser, Gerhard.Lausser@consol.de # Gregory Starck, g.starck@gmail.com # #This file is part of Shinken. # #Shinken is free software: you can redistribute it and/or modify #it under the terms of the GNU Affero General Public License as published by #the Free Software Foundation, either version 3 of the License, or #(at your option) any later version. # #Shinken is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU Affero General Public License for more details. # #You should have received a copy of the GNU Affero General Public License #along with Shinken. If not, see <http://www.gnu.org/licenses/>. #sudo nmap 192.168.0.1 -T4 -O --traceroute -oX toto.xml import optparse import sys import os import tempfile import subprocess try: # xml.etree.ElementTree is new in Python 2.5 from xml.etree.ElementTree import ElementTree except ImportError: sys.exit("This script needs the Python ElementTree module. Please install it") VERSION = '0.1' parser = optparse.OptionParser( "%prog [options] -t nmap scanning targets", version="%prog " + VERSION) parser.add_option('-t', '--targets', dest="targets", help="NMap scanning targets.") parser.add_option('-v', '--verbose', dest="verbose", action='store_true', help="Verbose output.") targets = [] opts, args = parser.parse_args() if not opts.targets: parser.error("Requires at least one nmap target for scanning (option -t/--targets") else: targets.append(opts.targets) if not opts.verbose: verbose = False else: verbose = True if args: targets.extend(args) print "Got our target", targets def debug(txt): if verbose: print txt # Says if a host is up or not def is_up(h): status = h.find('status') state = status.attrib['state'] return state == 'up' class DetectedHost: def __init__(self): self.ip = '' self.mac_vendor = '' self.host_name = '' self.os_possibilities = [] self.os = ('', '') self.open_ports = [] self.parent = '' # Keep the first name we've got def set_host_name(self, name): if self.host_name == '': self.host_name = name # Get a identifier for this host def get_name(self): if self.host_name != '': return self.host_name if self.ip != '': return self.ip return None # We look for the host VMWare def is_vmware_esx(self): # If it's not a virtual machine bail out if self.mac_vendor != 'VMware': return False # If we got all theses ports, we are quite ok for # a VMWare host needed_ports = [22, 80, 443, 902, 903, 5989] for p in needed_ports: if p not in self.open_ports: # find one missing port, not a VMWare host return False # Ok all ports are found, we are a ESX :) return True # Says if we are a virtual machine or not def is_vmware_vm(self): # special case : the esx host itself if self.is_vmware_esx(): return False # Else, look at the mac vendor return self.mac_vendor == 'VMware' # Fill the different os possibilities def add_os_possibility(self, os, osgen, accuracy): self.os_possibilities.append( (os, osgen, accuracy) ) # We search if our potential parent is present in the # other detected hosts. If so, set it as my parent def look_for_parent(self, all_hosts): self.parents = [] parent = self.parent debug("Look for my parent %s -> %s" % (self.get_name(), parent)) # Ok, we didn't find any parent # we bail out if parent == '': return for h in all_hosts: debug("Is it you? %s" % h.get_name()) if h.get_name() == parent: debug("Houray, we find our parent %s -> %s" % (self.get_name(), h.get_name())) self.parents.append(h.get_name()) # Look at ours oses and see which one is the better def compute_os(self): self.os_name = 'Unknown OS' self.os_version = 'Unknown Version' # bailout if we got no os :( if len(self.os_possibilities) == 0: return max_accuracy = 0 for (os, osgen, accuracy) in self.os_possibilities: if accuracy > max_accuracy: max_accuracy = accuracy # now get the entry with the max value for (os, osgen, accuracy) in self.os_possibilities: print "Can be", (os, osgen, accuracy) if accuracy == max_accuracy: self.os = (os, osgen) print "Try to match", self.os #Ok, unknown os... not good if self.os == ('', ''): return map = {('Windows', '2000') : 'windows', ('Windows', '2003') : 'windows', ('Windows', '7') : 'windows', ('Windows', 'XP') : 'windows', # ME? you are a stupid moron! ('Windows', 'Me') : 'windows', ('Windows', '2008') : 'windows', # that's a good boy :) ('Linux', '2.6.X') : 'linux', ('Linux', '2.4.X') : 'linux', # HPUX? I think you didn't choose... ('HP-UX', '11.X') : 'hpux', ('HP-UX', '10.X') : 'hpux', } if self.os not in map: return self.os_name = map[self.os] self.os_version = self.os[1] # self.templates.append(t) # # # Look for VMWare VM or hosts # if self.h.is_vmware_vm(): # self.templates.append('vmware-vm') # # Now is an host? # if self.h.is_vmware_esx(): # self.templates.append('vmware-host') # Return the string of the 'discovery' items def get_discovery_output(self): r = [] r.append('%s::isup=1' % self.get_name()) r.append(self.get_discovery_system()) r.append(self.get_discovery_macvendor()) op = self.get_discovery_ports() if op != '': r.append(op) par = self.get_discovery_parents() if par != '': r.append(par) fqdn = self.get_dicovery_fqdn() if fqdn != '': r.append(fqdn) ip = self.get_discovery_ip() if ip != '': r.append(ip) return r # for system output def get_discovery_system(self): r = '%s::os=%s' % (self.get_name(), self.os_name)+'\n' r += '%s::osversion=%s' % (self.get_name(), self.os_version) return r def get_discovery_macvendor(self): return '%s::macvendor=%s' % (self.get_name(), self.mac_vendor) def get_discovery_ports(self): if self.open_ports == []: return '' return '%s::openports=%s' % (self.get_name(), ','.join([str(p) for p in self.open_ports])) def get_discovery_parents(self): if self.parents == []: return '' return '%s::parents=%s' % (self.get_name(), ','.join(self.parents)) def get_dicovery_fqdn(self): if self.host_name == '': return '' return '%s::fqdn=%s' % (self.get_name(), self.host_name) def get_discovery_ip(self): if self.ip == '': return '' return '%s::ip=%s' % (self.get_name(), self.ip) (_, tmppath) = tempfile.mkstemp() print "propose a tmppath", tmppath cmd = "sudo nmap %s -T4 -O --traceroute -oX %s" % (' '.join(targets) , tmppath) print "Launching command,", cmd try: nmap_process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, shell=True) except OSError , exp: print "Debug : Error in launching command:", cmd, exp sys.exit(2) print "Try to communicate" (stdoutdata, stderrdata) = nmap_process.communicate() if nmap_process.returncode != 0: print "Error : the nmap return an error : '%s'" % stderrdata sys.exit(2) print "Got it", (stdoutdata, stderrdata) xml_input = tmppath tree = ElementTree() try: tree.parse(xml_input) except IOError, exp: print "Error opening file '%s' : %s" % (xml_input, exp) sys.exit(2) hosts = tree.findall('host') debug("Number of hosts : %d" % len(hosts)) all_hosts = [] for h in hosts: # Bypass non up hosts if not is_up(h): continue dh = DetectedHost() # Now we get the ipaddr and the mac vendor # for future VMWare matching #print h.__dict__ addrs = h.findall('address') for addr in addrs: #print "Address", addr.__dict__ addrtype = addr.attrib['addrtype'] if addrtype == 'ipv4': dh.ip = addr.attrib['addr'] if addrtype == "mac": if 'vendor' in addr.attrib: dh.mac_vendor = addr.attrib['vendor'] # Now we've got the hostnames host_names = h.findall('hostnames') for h_name in host_names: h_names = h_name.findall('hostname') for h_n in h_names: #print 'hname', h_n.__dict__ #print 'Host name', h_n.attrib['name'] dh.set_host_name(h_n.attrib['name']) # Now print the traceroute traces = h.findall('trace') for trace in traces: #print trace.__dict__ hops = trace.findall('hop') #print "Number of hops", len(hops) distance = len(hops) if distance >= 2: for hop in hops: ttl = int(hop.attrib['ttl']) #We search for the direct father if ttl == distance-1: #print ttl #print "Super hop", hop.__dict__ # Get the host name if possible, if not # take the IP if 'host' in hop.attrib: dh.parent = hop.attrib['host'] else: dh.parent = hop.attrib['ipaddr'] # Now the OS detection ios = h.find('os') #print os.__dict__ cls = ios.findall('osclass') for c in cls: #print "Class", c.__dict__ family = c.attrib['osfamily'] accuracy = c.attrib['accuracy'] if 'osgen' in c.attrib: osgen = c.attrib['osgen'] else: osgen = None #print "Type:", family, osgen, accuracy dh.add_os_possibility(family, osgen, accuracy) # Ok we can compute our OS now :) dh.compute_os() # Now the ports :) allports = h.findall('ports') for ap in allports: ports = ap.findall('port') for p in ports: #print "Port", p.__dict__ p_id = p.attrib['portid'] s = p.find('state') #print s.__dict__ state = s.attrib['state'] if state == 'open': dh.open_ports.append(int(p_id)) #print dh.__dict__ all_hosts.append(dh) #print "\n\n" for h in all_hosts: name = h.get_name() if not name: continue debug("Doing name %s" % name) #path = os.path.join(output_dir, name+'.discover') #print "Want path", path #f = open(path, 'wb') #cPickle.dump(h, f) #f.close() debug(str(h.__dict__)) # And generate the configuration too h.look_for_parent(all_hosts) #c.fill_system_conf() #c.fill_ports_services() #c.fill_system_services() # c.write_host_configuration() #print "Host config", c.get_cfg_for_host() # c.write_services_configuration() #print "Service config" #print c.get_cfg_for_services() #print c.__dict__ print '\n'.join(h.get_discovery_output()) #print "\n\n\n" # Try to remove the temppath try: os.unlink(tmppath) except Exception: pass