2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
7 #This file is part of Shinken.
9 #Shinken is free software: you can redistribute it and/or modify
10 #it under the terms of the GNU Affero General Public License as published by
11 #the Free Software Foundation, either version 3 of the License, or
12 #(at your option) any later version.
14 #Shinken is distributed in the hope that it will be useful,
15 #but WITHOUT ANY WARRANTY; without even the implied warranty of
16 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 #GNU Affero General Public License for more details.
19 #You should have received a copy of the GNU Affero General Public License
20 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
22 #sudo nmap 192.168.0.1 -T4 -O --traceroute -oX toto.xml
31 # xml.etree.ElementTree is new in Python 2.5
32 from xml
.etree
.ElementTree
import ElementTree
34 sys
.exit("This script needs the Python ElementTree module. Please install it")
38 parser
= optparse
.OptionParser(
39 "%prog [options] -t nmap scanning targets",
40 version
="%prog " + VERSION
)
42 parser
.add_option('-t', '--targets', dest
="targets",
43 help="NMap scanning targets.")
44 parser
.add_option('-v', '--verbose', dest
="verbose", action
='store_true',
45 help="Verbose output.")
48 opts
, args
= parser
.parse_args()
50 parser
.error("Requires at least one nmap target for scanning (option -t/--targets")
52 targets
.append(opts
.targets
)
62 print "Got our target", targets
68 # Says if a host is up or not
70 status
= h
.find('status')
71 state
= status
.attrib
['state']
82 self
.os_possibilities
= []
89 # Keep the first name we've got
90 def set_host_name(self
, name
):
91 if self
.host_name
== '':
95 # Get a identifier for this host
97 if self
.host_name
!= '':
103 # We look for the host VMWare
104 def is_vmware_esx(self
):
105 # If it's not a virtual machine bail out
106 if self
.mac_vendor
!= 'VMware':
108 # If we got all theses ports, we are quite ok for
110 needed_ports
= [22, 80, 443, 902, 903, 5989]
111 for p
in needed_ports
:
112 if p
not in self
.open_ports
:
113 # find one missing port, not a VMWare host
115 # Ok all ports are found, we are a ESX :)
118 # Says if we are a virtual machine or not
119 def is_vmware_vm(self
):
120 # special case : the esx host itself
121 if self
.is_vmware_esx():
123 # Else, look at the mac vendor
124 return self
.mac_vendor
== 'VMware'
127 # Fill the different os possibilities
128 def add_os_possibility(self
, os
, osgen
, accuracy
):
129 self
.os_possibilities
.append( (os
, osgen
, accuracy
) )
132 # We search if our potential parent is present in the
133 # other detected hosts. If so, set it as my parent
134 def look_for_parent(self
, all_hosts
):
137 debug("Look for my parent %s -> %s" % (self
.get_name(), parent
))
138 # Ok, we didn't find any parent
143 debug("Is it you? %s" % h
.get_name())
144 if h
.get_name() == parent
:
145 debug("Houray, we find our parent %s -> %s" % (self
.get_name(), h
.get_name()))
146 self
.parents
.append(h
.get_name())
151 # Look at ours oses and see which one is the better
152 def compute_os(self
):
153 self
.os_name
= 'Unknown OS'
154 self
.os_version
= 'Unknown Version'
156 # bailout if we got no os :(
157 if len(self
.os_possibilities
) == 0:
161 for (os
, osgen
, accuracy
) in self
.os_possibilities
:
162 if accuracy
> max_accuracy
:
163 max_accuracy
= accuracy
165 # now get the entry with the max value
166 for (os
, osgen
, accuracy
) in self
.os_possibilities
:
167 if accuracy
== max_accuracy
:
168 self
.os
= (os
, osgen
)
172 #Ok, unknown os... not good
173 if self
.os
== ('', ''):
176 map = {('Windows', '2000') : 'windows',
177 ('Windows', '2003') : 'windows',
178 ('Windows', '7') : 'windows',
179 ('Windows', 'XP') : 'windows',
180 # ME? you are a stupid moron!
181 ('Windows', 'Me') : 'windows',
182 ('Windows', '2008') : 'windows',
183 # that's a good boy :)
184 ('Linux', '2.6.X') : 'linux',
185 ('Linux', '2.4.X') : 'linux',
186 # HPUX? I think you didn't choose...
187 ('HP-UX', '11.X') : 'hpux',
188 ('HP-UX', '10.X') : 'hpux',
191 if self
.os
not in map:
194 self
.os_name
= map[self
.os
]
195 self
.os_version
= self
.os
[1]
196 # self.templates.append(t)
198 # # Look for VMWare VM or hosts
199 # if self.h.is_vmware_vm():
200 # self.templates.append('vmware-vm')
202 # if self.h.is_vmware_esx():
203 # self.templates.append('vmware-host')
206 # Return the string of the 'discovery' items
207 def get_discovery_output(self
):
209 r
.append('%s::isup=1' % self
.get_name())
210 r
.append(self
.get_discovery_system())
211 r
.append(self
.get_discovery_macvendor())
212 op
= self
.get_discovery_ports()
215 par
= self
.get_discovery_parents()
218 fqdn
= self
.get_dicovery_fqdn()
221 ip
= self
.get_discovery_ip()
228 def get_discovery_system(self
):
229 r
= '%s::os=%s' % (self
.get_name(), self
.os_name
)+'\n'
230 r
+= '%s::osversion=%s' % (self
.get_name(), self
.os_version
)
233 def get_discovery_macvendor(self
):
234 return '%s::macvendor=%s' % (self
.get_name(), self
.mac_vendor
)
236 def get_discovery_ports(self
):
237 if self
.open_ports
== []:
239 return '%s::openports=%s' % (self
.get_name(), ','.join([str(p
) for p
in self
.open_ports
]))
241 def get_discovery_parents(self
):
242 if self
.parents
== []:
244 return '%s::parents=%s' % (self
.get_name(), ','.join(self
.parents
))
246 def get_dicovery_fqdn(self
):
247 if self
.host_name
== '':
249 return '%s::fqdn=%s' % (self
.get_name(), self
.host_name
)
251 def get_discovery_ip(self
):
254 return '%s::ip=%s' % (self
.get_name(), self
.ip
)
257 (_
, tmppath
) = tempfile
.mkstemp()
259 print "propose a tmppath", tmppath
261 cmd
= "sudo nmap %s -T4 -O --traceroute -oX %s" % (' '.join(targets
) , tmppath
)
262 print "Launching command,", cmd
264 nmap_process
= subprocess
.Popen(
266 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
267 close_fds
=True, shell
=True)
268 except OSError , exp
:
269 print "Debug : Error in launching command:", cmd
, exp
272 print "Try to communicate"
273 (stdoutdata
, stderrdata
) = nmap_process
.communicate()
275 if nmap_process
.returncode
!= 0:
276 print "Error : the nmap return an error : '%s'" % stderrdata
279 print "Got it", (stdoutdata
, stderrdata
)
285 tree
.parse(xml_input
)
287 print "Error opening file '%s' : %s" % (xml_input
, exp
)
290 hosts
= tree
.findall('host')
291 debug("Number of hosts : %d" % len(hosts
))
297 # Bypass non up hosts
303 # Now we get the ipaddr and the mac vendor
304 # for future VMWare matching
306 addrs
= h
.findall('address')
308 #print "Address", addr.__dict__
309 addrtype
= addr
.attrib
['addrtype']
310 if addrtype
== 'ipv4':
311 dh
.ip
= addr
.attrib
['addr']
312 if addrtype
== "mac":
313 if 'vendor' in addr
.attrib
:
314 dh
.mac_vendor
= addr
.attrib
['vendor']
317 # Now we've got the hostnames
318 host_names
= h
.findall('hostnames')
319 for h_name
in host_names
:
320 h_names
= h_name
.findall('hostname')
322 #print 'hname', h_n.__dict__
323 #print 'Host name', h_n.attrib['name']
324 dh
.set_host_name(h_n
.attrib
['name'])
327 # Now print the traceroute
328 traces
= h
.findall('trace')
330 #print trace.__dict__
331 hops
= trace
.findall('hop')
332 #print "Number of hops", len(hops)
336 ttl
= int(hop
.attrib
['ttl'])
337 #We search for the direct father
338 if ttl
== distance
-1:
340 #print "Super hop", hop.__dict__
341 # Get the host name if possible, if not
343 if 'host' in hop
.attrib
:
344 dh
.parent
= hop
.attrib
['host']
346 dh
.parent
= hop
.attrib
['ipaddr']
349 # Now the OS detection
352 cls
= ios
.findall('osclass')
354 #print "Class", c.__dict__
355 family
= c
.attrib
['osfamily']
356 accuracy
= c
.attrib
['accuracy']
357 if 'osgen' in c
.attrib
:
358 osgen
= c
.attrib
['osgen']
361 #print "Type:", family, osgen, accuracy
362 dh
.add_os_possibility(family
, osgen
, accuracy
)
363 # Ok we can compute our OS now :)
368 allports
= h
.findall('ports')
370 ports
= ap
.findall('port')
372 #print "Port", p.__dict__
373 p_id
= p
.attrib
['portid']
376 state
= s
.attrib
['state']
378 dh
.open_ports
.append(int(p_id
))
391 debug("Doing name %s" % name
)
392 #path = os.path.join(output_dir, name+'.discover')
393 #print "Want path", path
394 #f = open(path, 'wb')
397 debug(str(h
.__dict
__))
398 # And generate the configuration too
399 h
.look_for_parent(all_hosts
)
400 #c.fill_system_conf()
401 #c.fill_ports_services()
402 #c.fill_system_services()
403 # c.write_host_configuration()
404 #print "Host config", c.get_cfg_for_host()
405 # c.write_services_configuration()
406 #print "Service config"
407 #print c.get_cfg_for_services()
409 print '\n'.join(h
.get_discovery_output())
413 # Try to remove the temppath