class NMAPXMLOutputFile(object): def __init__(self,path): self.path = path try: self.tree = etree.parse(self.path) except etree.XMLSyntaxError,e: raise ReportParserError('Error parsing %s: %s' % (self.path,e)) root = self.tree.getroot() if root.tag != 'nmaprun': raise ReportParserError('Input is not supported NMAP XML output file') self.scanner = root.get('scanner') self.version = root.get('version') self.args = root.get('args') self.start_ts = int(root.get('start')) self.scaninfo = NMAPScanInfo(self.tree.find('scaninfo')) self.runstats = NMAPRunStats(self.tree.find('runstats')) try: self.hosts = map(lambda h: NMAPTargetHostEntry(h), self.tree.findall('host') ) except ReportParserError,e: raise ReportParserError('Error parsing %s: %s' % (self.path,e))
def __init__(self, path): if not os.path.isfile(path): raise ReportParserError('No such file: %s' % path) self.path = path try: self.tree = etree.parse(path) except etree.XMLSyntaxError, e: raise ReportParserError('Error parsing %s: %s' % (self.path, e))
def __resolve_level(self,level): if level in self['levels'].keys(): return level try: level = int(level) return filter(lambda l: level==self['levels'][l]['level'], self['levels'].keys() )[0] except IndexError: raise ReportParserError('Invalid level: %s' % level) except ValueError: raise ReportParserError('Invalid level: %s' % level)
def __init__(self, host, node): self.host = host self.node = node self.name = node.get('name') self.update(node.items()) for k in filter(lambda k: self.has_key(k), REPORT_INT_VALUES): try: self[k] = int(self[k]) except ValueError: raise ReportParserError('Invalid integer value: %s' % self[k]) for n in node.getchildren(): if n.tag in REPORT_INT_VALUES: try: self[n.tag] = int(n.text) except ValueError: raise ReportParserError( 'Invalid integer value %s: %s' % n.tag, n.text) elif n.tag in REPORT_DECIMAL_VALUES: try: self[n.tag] = decimal.Decimal(n.text) except ValueError: raise ReportParserError('Invalid decimal value %s: %s' % (n.tag, n.text)) elif n.tag in REPORT_TEXT_VALUES: self[n.tag] = [] for l in n.text.split('\n'): if l.strip() == '': continue self[n.tag].append(l) elif n.tag in REPORT_DATE_VALUES: dates = [] for d in filter(lambda d: d.strip(), n.text.split()): try: dates.append(time.strptime(d, '%Y/%m/%d')) except ValueError, e: raise ReportParserError('Invalid date %s: %s' % (k, d)) self[n.tag] = dates elif n.tag in REPORT_BOOLEAN_VALUES: if n.text.lower() in ['true', 'yes']: self[n.tag] = True else: self[n.tag] = False
class NessusXMLReport(list): def __init__(self, path): if not os.path.isfile(path): raise ReportParserError('No such file: %s' % path) self.path = path try: self.tree = etree.parse(path) except etree.XMLSyntaxError, e: raise ReportParserError('Error parsing %s: %s' % (self.path, e)) root = self.tree.getroot() if root.tag not in NESSUS_REPORT_FORMATS: raise ReportParserError('Unsupported nessus report format: %s' % root.tag) self.preferences = NessusReportPreferences( self.tree.find('Preferences')) self.target_families = NessusTargetFamilies( self.tree.find('FamilySelection')) self.plugins = NessusPluginList( self.tree.find('IndividualPluginSelection')) for node in self.tree.findall('Report'): # May raise ReportParserError self.append(NessusReport(self, node))
def background(self,level): if level == 'header': return self['header']['background'] level = self.__resolve_level(level) try: return self['levels'][level]['background'] except KeyError: raise ReportParserError('No background defined for level %s' % level)
def __init__(self,node): self.finished = dict(node.find('finished').items()) self.hosts = dict(node.find('hosts').items()) for k in self.hosts.keys(): try: self.hosts[k] = int(self.hosts[k]) except ValueError: raise ReportParserError('Runstats hosts attribute %s not integer' % k)
def color(self,level): if level == 'header': return self['header']['color'] level = self.__resolve_level(level) try: return self['levels'][level]['color'] except KeyError: raise ReportParserError('No color defined for level %s' % level)
def merge_pluginlist_file(self, path, filtered_ids): try: for l in open(path, 'r').readlines(): if l.startswith('#'): continue (pid, description) = l.strip().split(None, 1) try: filtered_ids.add(int(pid)) except ValueError: sys.exit(error('Invalid Plugin ID: %s' % pid)) except IOError, (ecode, emsg): raise ReportParserError( 'Error reading filtered plugin list file %s %s' % (opts.filter_plugins, emsg))
def load_addresslist(self, values): self.log.debug('Loading address list %s' % values) addresses = [] for address in values: try: address = IPv4Address(address) except ValueError: try: address = IPv6Address(address) except ValueError: raise ReportParserError('Invalid address: %s' % address) addresses.append(address) return addresses
def __init__(self, path): self.path = path self.update(dict((k, {}) for k in SEVERITY_MAP.keys())) if not os.path.isfile(self.path): raise ReportParserError('No such file: %s' % self.path) self.parser = BeautifulSoup(markup=open(self.path, 'r').read()) contents = self.parser.find('div', {'id': 'contents'}) if contents is None: raise ReportParserError('No table of contents found') self.device = None self.name = None t = self.parser.find('title').text for re_match in DEVICE_TITLES: m = re_match.match(t) if m: self.device = m.group(1) self.name = m.group(2) break if self.device is None or self.name is None: raise ReportParserError('Could not parse device type and name') for d in self.parser.findChildren('div'): d_id = d.get('id') if d_id is None or d_id in SKIP_DIVS: continue r = NipperReportedIssue(self, d) try: severity = filter(lambda k: r.severity in SEVERITY_MAP[k], SEVERITY_MAP.keys())[0] except IndexError: ReportParserError('Unknown severity level: %s' % r.severity) if not self[severity].has_key(r.issue): self[severity][r.issue] = [] self[severity][r.issue].append(r)
def __init__(self, report, node): self.report = report self.node = node self.address = node.get('name') self.properties = NessusTargetHostProperties( node.find('HostProperties')) if self.address is None: raise ReportParserError('No address') try: self.address = IPv4Address(self.address) except ValueError: try: self.address = IPv6Address(self.address) except ValueError: self.address = IPv4Address( socket.gethostbyaddr(self.address)[2][0]) try: for i in self.node.findall('ReportItem'): self.append(NessusTargetResultItem(self, i)) except ReportParserError, e: raise ReportParserError('Error parsing %s: %s' % (self.address, e))
def __init__(self,node): self.nmapscans = [ NMAPHostScan(node) ] try: self.ports = map(lambda p: NMAPTargetPortEntry(p), node.find('ports').findall('port'), ) self.addresses = map(lambda a: NMAPHostAddressEntry(a), node.findall('address'), ) self.osinfo = NMAPHostOSGuesses(node.find('os')) except AttributeError: raise ReportParserError( 'No ports or addresses in target entry %s' % node.items() )
def __init__(self,node): self.update(node.items()) if self.has_key('numservices'): self['numservices'] = int(self['numservices']) if self.has_key('services'): services = [] try: service_list = self['services'].split(',') for s in service_list: try: start,end = map(lambda x: int(x), s.split('-')) for i in range(start,end+1): services.append(i) except ValueError: try: services.append(int(s)) except ValueError: raise ValueError except ValueError: raise ReportParserError('Error parsing services: %s' % self['services']) self['services'] = services
def __init__(self, report, section): self.report = report m = RE_ISSUE_HEADER.match(section.find('h3').text) if not m: raise ReportParserError('Could not parse report h3 header: %s' % section) self.issue = m.group(1) for sub in section.findChildren('div'): sub_id = sub.get('class') if sub_id == 'ratings': self['severity'] = NipperIssueRatings(sub) elif sub_id == 'finding': self['finding'] = NipperIssueFinding(sub) elif sub_id == 'impact': self['impoct'] = NipperIssueImpact(sub) elif sub_id == 'ease': self['ease'] = NipperIssueEase(sub) elif sub_id == 'recommendation': self['recommendation'] = NipperIssueRecommendation(sub) else: print sub_id
def read(self,path): try: entry = NMAPXMLOutputFile(path) except ReportParserError,e: raise ReportParserError(e)
def __init__(self,path): self.path = path try: self.tree = etree.parse(self.path) except etree.XMLSyntaxError,e: raise ReportParserError('Error parsing %s: %s' % (self.path,e))
class NessusResultSet(list): def __init__(self): self.log = logging.getLogger('modules') self.pluginid_hostmap = {} def __sortkeys__(self, *argv): return lambda mapping: tuple(-mapping[name[1:]] if name.startswith('-') else mapping[name] for name in argv) def load(self, reports, filtered, addresses=[]): networks = filter(lambda address: (type(address)==IPv4Address and address.bitmask!=32) or\ (type(address)==IPv6Address and address.bitmask!=128), addresses ) def match_address(address, addresses, networks): if address in addresses: return True networks = filter(lambda n: type(n) == type(address), networks) for n in networks: if n.addressInNetwork(address): return True return False for source in reports: self.log.debug('Merging report with %d plugin IDs filtered: %s' % (len(filtered), source)) filtered_count = 0 filter_address_count = 0 for r in [ result for report in source for host in report for result in host ]: if r.pluginID in filtered: filtered_count += 1 continue if addresses != [] and not match_address( r.address, addresses, networks): filter_address_count += 1 continue if not self.pluginid_hostmap.has_key(r.pluginID): self.pluginid_hostmap[r.pluginID] = [] if r.port != 0: r_key = '%s:%s' % (r.address.ipaddress, r.port) else: r_key = '%s' % r.address.ipaddress if r_key not in self.pluginid_hostmap[r.pluginID]: self.pluginid_hostmap[r.pluginID].append(r_key) self.append(r) if addresses != []: self.log.debug('Filtered out %d plugins %d addresses' % (filtered_count, filter_address_count)) else: self.log.debug('Filtered out %d plugins' % filtered_count) def order_by(self, *argv): self.log.debug('Ordering results') decorated = [([ -result[k[1:]] if k.startswith('-') else result[k] for k in argv ], index, result) for index, result in enumerate(self)] decorated.sort() self.__delslice__(0, len(self)) self.extend([d[-1] for d in decorated]) def pluginid_hosts(self, result): self.log.debug('Grouping hosts for plugin: %s %s' % (SEVERITY_NAMES[result.severity], result.pluginName)) return [ h.ipaddress for h in sorted( set(r.address for r in self if r.pluginID == result.pluginID)) ] def filter(self, fn): self.log.debug('Filtering %d results' % len(self)) total = len(self) processed = 0 for r in self: processed += 1 if not fn(r): self.remove(r) if processed % 1000 == 0: self.log.debug('Processed: %d/%d results' % (processed, total)) def counters(self): values = dict([(r, 0) for r in range(0, 4)]) for r in self: values[r.severity] += 1 return values def merge_pluginlist_file(self, path, filtered_ids): try: for l in open(path, 'r').readlines(): if l.startswith('#'): continue (pid, description) = l.strip().split(None, 1) try: filtered_ids.add(int(pid)) except ValueError: sys.exit(error('Invalid Plugin ID: %s' % pid)) except IOError, (ecode, emsg): raise ReportParserError( 'Error reading filtered plugin list file %s %s' % (opts.filter_plugins, emsg)) except OSError, (ecode, emsg): raise ReportParserError( 'Error reading filtered plugin list file %s %s' % (opts.filter_plugins, emsg))
class NessusTargetResultItem(dict): def __init__(self, host, node): self.host = host self.node = node self.name = node.get('name') self.update(node.items()) for k in filter(lambda k: self.has_key(k), REPORT_INT_VALUES): try: self[k] = int(self[k]) except ValueError: raise ReportParserError('Invalid integer value: %s' % self[k]) for n in node.getchildren(): if n.tag in REPORT_INT_VALUES: try: self[n.tag] = int(n.text) except ValueError: raise ReportParserError( 'Invalid integer value %s: %s' % n.tag, n.text) elif n.tag in REPORT_DECIMAL_VALUES: try: self[n.tag] = decimal.Decimal(n.text) except ValueError: raise ReportParserError('Invalid decimal value %s: %s' % (n.tag, n.text)) elif n.tag in REPORT_TEXT_VALUES: self[n.tag] = [] for l in n.text.split('\n'): if l.strip() == '': continue self[n.tag].append(l) elif n.tag in REPORT_DATE_VALUES: dates = [] for d in filter(lambda d: d.strip(), n.text.split()): try: dates.append(time.strptime(d, '%Y/%m/%d')) except ValueError, e: raise ReportParserError('Invalid date %s: %s' % (k, d)) self[n.tag] = dates elif n.tag in REPORT_BOOLEAN_VALUES: if n.text.lower() in ['true', 'yes']: self[n.tag] = True else: self[n.tag] = False elif n.tag == 'plugin_version': plugin_version = None for rev in NESSUS_PLUGIN_REVISION_MATCHES: m = rev.match(n.text) if not m: continue plugin_version = m.group(1) break if plugin_version is None: raise ReportParserError('Unknown plugin version %s' % pv) self['plugin_version'] = plugin_version
elif n.tag == 'plugin_version': plugin_version = None for rev in NESSUS_PLUGIN_REVISION_MATCHES: m = rev.match(n.text) if not m: continue plugin_version = m.group(1) break if plugin_version is None: raise ReportParserError('Unknown plugin version %s' % pv) self['plugin_version'] = plugin_version elif n.tag == 'plugin_type': if n.text not in NESSUS_PLUGIN_TYPES: raise ReportParserError('Unknown plugin types: %s' % n.text) self[n.tag] = n.text elif n.tag == 'solution': if n.text.lower() in ['n/a', '']: self['solution'] = None else: self['solution'] = n.text elif n.tag == 'risk_factor': if n.text in ['None', '']: self[n.tag] = None else: self[n.tag] = n.text elif n.tag in REPORT_REFERENCE_FIELDS: