# -*- coding: utf-8 -*-
import warnings
from libnmap.objects.cpe import CPE
[docs]class OSFPPortUsed(object):
"""
Port used class: this enables the user of NmapOSFingerprint class
to have a common and clear interface to access portused data which
were collected and used during os fingerprint scan
"""
def __init__(self, port_used_dict):
try:
self._state = port_used_dict['state']
self._proto = port_used_dict['proto']
self._portid = port_used_dict['portid']
except KeyError:
raise Exception("Cannot create OSFPPortUsed: missing required key")
@property
def state(self):
"""
Accessor for the portused state (closed, open,...)
"""
return self._state
@property
def proto(self):
"""
Accessor for the portused protocol (tcp, udp,...)
"""
return self._proto
@property
def portid(self):
"""
Accessor for the referenced port number used
"""
return self._portid
[docs]class NmapOSMatch(object):
"""
NmapOSMatch is an internal class used for offering results
from an nmap os fingerprint. This common interfaces makes
a compatibility between old nmap xml (<1.04) and new nmap
xml versions (used in nmapv6 for instance).
In previous xml version, osclass tags from nmap fingerprints
were not directly mapped to a osmatch. In new xml version,
osclass could be embedded in osmatch tag.
The approach to solve this is to create a common class
which will, for older xml version, match based on the accuracy
osclass to an osmatch. If no match, an osmatch will be made up
from a concat of os class attributes: vendor and osfamily.
Unmatched osclass will have a line attribute of -1.
More info, see issue #26 or http://seclists.org/nmap-dev/2012/q2/252
"""
def __init__(self, osmatch_dict):
_osmatch_dict = osmatch_dict['osmatch']
if('name' not in _osmatch_dict or
'line' not in _osmatch_dict or
'accuracy' not in _osmatch_dict):
raise Exception("Cannot create NmapOSClass: missing required key")
self._name = _osmatch_dict['name']
self._line = _osmatch_dict['line']
self._accuracy = _osmatch_dict['accuracy']
# create osclass list
self._osclasses = []
try:
for _osclass in osmatch_dict['osclasses']:
try:
_osclassobj = NmapOSClass(_osclass)
except:
raise Exception("Could not create NmapOSClass object")
self._osclasses.append(_osclassobj)
except KeyError:
pass
[docs] def add_osclass(self, osclass_obj):
"""
Add a NmapOSClass object to the OSMatch object. This method is
useful to implement compatibility with older versions of NMAP
by providing a common interface to access os fingerprint data.
"""
self._osclasses.append(osclass_obj)
@property
def osclasses(self):
"""
Accessor for all NmapOSClass objects matching with this OS Match
"""
return self._osclasses
@property
def name(self):
"""
Accessor for name attribute (e.g.: Linux 2.4.26 (Slackware 10.0.0))
"""
return self._name
@property
def line(self):
"""
Accessor for line attribute as integer. value equals -1 if this
osmatch holds orphans NmapOSClass objects. This could happen with
older version of nmap xml engine (<1.04 (e.g: nmapv6)).
:return: int
"""
return int(self._line)
@property
def accuracy(self):
"""
Accessor for accuracy
:return: int
"""
return int(self._accuracy)
[docs] def get_cpe(self):
"""
This method return a list of cpe stings and not CPE objects as
the NmapOSClass.cpelist property. This method is a helper to
simplify data management.
For more advanced handling of CPE data, use NmapOSClass.cpelist
and use the methods from CPE class
"""
_cpelist = []
for osc in self.osclasses:
for cpe in osc.cpelist:
_cpelist.append(cpe.cpestring)
return _cpelist
def __repr__(self):
rval = "{0}: {1}".format(self.name, self.accuracy)
for _osclass in self._osclasses:
rval += "\r\n |__ os class: {0}".format(str(_osclass))
return rval
[docs]class NmapOSClass(object):
"""
NmapOSClass offers an unified API to access data from analysed
osclass tag. As implemented in libnmap and newer version of nmap,
osclass objects will always be embedded in a NmapOSMatch.
Unmatched NmapOSClass will be stored in "dummy" NmapOSMatch objects
which will have the particularity of have a line attribute of -1.
On top of this, NmapOSClass will have optional CPE objects
embedded.
"""
def __init__(self, osclass_dict):
_osclass = osclass_dict['osclass']
if('vendor' not in _osclass or
'osfamily' not in _osclass or
'accuracy' not in _osclass):
raise Exception("Wrong osclass structure: missing required key")
self._vendor = _osclass['vendor']
self._osfamily = _osclass['osfamily']
self._accuracy = _osclass['accuracy']
self._osgen = ''
self._type = ''
# optional data
if 'osgen' in _osclass:
self._osgen = _osclass['osgen']
if 'type' in _osclass:
self._type = _osclass['type']
self._cpelist = []
for _cpe in osclass_dict['cpe']:
self._cpelist.append(CPE(_cpe))
@property
def cpelist(self):
"""
Returns a list of CPE Objects matching with this os class
:return: list of CPE objects
:rtype: Array
"""
return self._cpelist
@property
def vendor(self):
"""
Accessor for vendor information (Microsoft, Linux,...)
:return: string
"""
return self._vendor
@property
def osfamily(self):
"""
Accessor for OS family information (Windows, Linux,...)
:return: string
"""
return self._osfamily
@property
def accuracy(self):
"""
Accessor for OS class detection accuracy (int)
:return: int
"""
return int(self._accuracy)
@property
def osgen(self):
"""
Accessor for OS class generation (7, 8, 2.4.X,...).
:return: string
"""
return self._osgen
@property
def type(self):
"""
Accessor for OS class type (general purpose,...)
:return: string
"""
return self._type
@property
def description(self):
"""
Accessor helper which returns a concataned string of
the valuable attributes from NmapOSClass object
:return: string
"""
rval = "{0}: {1}, {2}".format(self.type, self.vendor, self.osfamily)
if len(self.osgen):
rval += "({0})".format(self.osgen)
return rval
def __repr__(self):
rval = "{0}: {1}, {2}".format(self.type, self.vendor, self.osfamily)
if len(self.osgen):
rval += "({0})".format(self.osgen)
for _cpe in self._cpelist:
rval += "\r\n |__ {0}".format(str(_cpe))
return rval
[docs]class NmapOSFingerprint(object):
"""
NmapOSFingerprint is a easier API for using os fingerprinting.
Data for OS fingerprint (<os> tag) is instanciated from
a NmapOSFingerprint which is accessible in NmapHost via NmapHost.os
"""
def __init__(self, osfp_data):
self.__osmatches = []
self.__ports_used = []
self.__fingerprints = []
if 'osmatches' in osfp_data:
for _osmatch in osfp_data['osmatches']:
_osmatch_obj = NmapOSMatch(_osmatch)
self.__osmatches.append(_osmatch_obj)
if 'osclasses' in osfp_data:
for _osclass in osfp_data['osclasses']:
_osclass_obj = NmapOSClass(_osclass)
_osmatched = self.get_osmatch(_osclass_obj)
if _osmatched is not None:
_osmatched.add_osclass(_osclass_obj)
else:
self._add_dummy_osmatch(_osclass_obj)
if 'osfingerprints' in osfp_data:
for _osfp in osfp_data['osfingerprints']:
if 'fingerprint' in _osfp:
self.__fingerprints.append(_osfp['fingerprint'])
if 'ports_used' in osfp_data:
for _pused_dict in osfp_data['ports_used']:
_pused = OSFPPortUsed(_pused_dict)
self.__ports_used.append(_pused)
[docs] def get_osmatch(self, osclass_obj):
"""
This function enables NmapOSFingerprint to determine if an
NmapOSClass object could be attached to an existing NmapOSMatch
object in order to respect the common interface for
the nmap xml version < 1.04 and >= 1.04
This method will return an NmapOSMatch object matching with
the NmapOSClass provided in parameter
(match is performed based on accuracy)
:return: NmapOSMatch object
"""
rval = None
for _osmatch in self.__osmatches:
if _osmatch.accuracy == osclass_obj.accuracy:
rval = _osmatch
break # sorry
return rval
def _add_dummy_osmatch(self, osclass_obj):
"""
This functions creates a dummy NmapOSMatch object in order to
encapsulate an NmapOSClass object which was not matched with an
existing NmapOSMatch object
"""
_dname = "{0}:{1}:{2}".format(osclass_obj.type,
osclass_obj.vendor,
osclass_obj.osfamily)
_dummy_dict = {'osmatch': {'name': _dname,
'accuracy': osclass_obj.accuracy,
'line': -1},
'osclasses': []}
_dummy_osmatch = NmapOSMatch(_dummy_dict)
self.__osmatches.append(_dummy_osmatch)
@property
def osmatches(self, min_accuracy=0):
_osmatches = []
for _osmatch in self.__osmatches:
if _osmatch.accuracy >= min_accuracy:
_osmatches.append(_osmatch)
return _osmatches
@property
def osclasses(self, min_accuracy=0):
osc_array = []
for _osm in self.osmatches:
for _osc in _osm.osclasses:
if _osc.accuracy >= min_accuracy:
osc_array.append(_osc)
return osc_array
@property
def fingerprint(self):
return "\r\n".join(self.__fingerprints)
@property
def fingerprints(self):
return self.__fingerprints
@property
def ports_used(self):
"""
Return an array of OSFPPortUsed object with the ports used to
perform the os fingerprint. This dict might contain another dict
embedded containing the ports_reason values.
"""
return self.__ports_used
def osmatch(self, min_accuracy=90):
warnings.warn("NmapOSFingerprint.osmatch is deprecated: "
"use NmapOSFingerprint.osmatches", DeprecationWarning)
os_array = []
for _osmatch in self.__osmatches:
if _osmatch.accuracy >= min_accuracy:
os_array.append(_osmatch.name)
return os_array
def osclass(self, min_accuracy=90):
warnings.warn("NmapOSFingerprint.osclass() is deprecated: "
"use NmapOSFingerprint.osclasses() if applicable",
DeprecationWarning)
os_array = []
for osmatch_entry in self.osmatches():
if osmatch_entry.accuracy >= min_accuracy:
for oclass in osmatch_entry.osclasses:
_ftstr = "type:{0}|vendor:{1}|osfamily{2}".format(
oclass.type,
oclass.vendor,
oclass.osfamily)
os_array.append(_ftstr)
return os_array
def os_cpelist(self):
cpelist = []
for _osmatch in self.osmatches:
for oclass in _osmatch.osclasses:
cpelist.extend(oclass.cpelist)
return cpelist
def __repr__(self):
rval = ""
for _osmatch in self.osmatches:
rval += "\r\n{0}".format(_osmatch)
rval += "Fingerprints: ".format(self.fingerprint)
return rval