Пример #1
0
def parse(project, nessus_file, include_informational=False, min_note_sev=2):
    """Parses a Nessus XMLv2 file and updates the Hive database

    :param project: The project id
    :param nessus_file: The Nessus xml file to be parsed
    :param include_informational: Whether to include info findings in data. Default False
    :min_note_sev: The minimum severity of notes that will be saved. Default 2
    """

    cve_pattern = re.compile(r'(CVE-|CAN-)')
    false_udp_pattern = re.compile(r'.*\?$')

    tree = et.parse(nessus_file)
    root = tree.getroot()
    note_id = 1

    # Create the project dictionary which acts as foundation of document
    project_dict = dict(models.project_model)
    project_dict['commands'] = list()
    project_dict['vulnerabilities'] = list()
    project_dict['project_id'] = project

    # Used to maintain a running list of host:port vulnerabilities by plugin
    vuln_host_map = dict()

    for host in root.iter('ReportHost'):

        temp_ip = host.attrib['name']

        host_dict = dict(models.host_model)
        host_dict['os'] = list()
        host_dict['ports'] = list()
        host_dict['hostnames'] = list()

        # Tags contain host-specific information
        for tag in host.iter('tag'):

            # Operating system tag
            if tag.attrib['name'] == 'operating-system':
                os_dict = dict(models.os_model)
                os_dict['tool'] = TOOL
                os_dict['weight'] = OS_WEIGHT
                os_dict['fingerprint'] = tag.text
                host_dict['os'].append(os_dict)

            # IP address tag
            if tag.attrib['name'] == 'host-ip':
                host_dict['string_addr'] = tag.text
                host_dict['long_addr'] = helper.ip2long(tag.text)

            # MAC address tag
            if tag.attrib['name'] == 'mac-address':
                host_dict['mac_addr'] = tag.text

            # Hostname tag
            if tag.attrib['name'] == 'host-fqdn':
                host_dict['hostnames'].append(tag.text)

            # NetBIOS name tag
            if tag.attrib['name'] == 'netbios-name':
                host_dict['hostnames'].append(tag.text)

        # Track the unique port/protocol combos for a host so we don't
        # add duplicate entries
        ports_processed = dict()

        # Process each 'ReportItem'
        for item in host.findall('ReportItem'):
            plugin_id = item.attrib['pluginID']
            plugin_family = item.attrib['pluginFamily']
            severity = int(item.attrib['severity'])
            title = item.attrib['pluginName']

            port = int(item.attrib['port'])
            protocol = item.attrib['protocol']
            service = item.attrib['svc_name']
            evidence = item.find('plugin_output')

            # Ignore false positive UDP services
            if protocol == "udp" and false_udp_pattern.match(service):
                continue

            # Create a port model and temporarily store it in the dict
            # for tracking purposes. The ports_processed dict is used
            # later to add ports to the host so that no duplicates are
            # present. This is necessary due to the format of the Nessus
            # XML files.
            if '{0}:{1}'.format(port, protocol) not in ports_processed:
                port_dict = copy.deepcopy(models.port_model)
                port_dict['port'] = port
                port_dict['protocol'] = protocol
                port_dict['service'] = service
                ports_processed['{0}:{1}'.format(port, protocol)] = port_dict

            # Set the evidence as a port note if it exists
            if evidence is not None and \
                    severity >= min_note_sev and \
                    plugin_family != 'Port scanners' and \
                    plugin_family != 'Service detection':
                note_dict = copy.deepcopy(models.note_model)
                note_dict['title'] = "{0} (ID{1})".format(title, str(note_id))
                e = evidence.text.strip()
                for line in e.split("\n"):
                    line = line.strip()
                    if line:
                        note_dict['content'] += "    " + line + "\n"
                note_dict['last_modified_by'] = TOOL
                ports_processed['{0}:{1}'.format(port, protocol)]['notes'].append(note_dict)
                note_id += 1

            # This plugin is general scan info...use it for 'command' element
            if plugin_id == '19506':

                command = item.find('plugin_output')

                command_dict = dict(models.command_model)
                command_dict['tool'] = TOOL

                if command is not None:
                    command_dict['command'] = command.text

                if not project_dict['commands']:
                    project_dict['commands'].append(command_dict)

                continue

            # Check if this vulnerability has been seen in this file for
            # another host. If not, create a new vulnerability_model and
            # maintain a mapping between plugin-id and vulnerability as
            # well as a mapping between plugin-id and host. These mappings
            # are later used to completed the Hive schema such that host
            # IP and port information are embedded within each vulnerability
            # while ensuring no duplicate data exists.
            if plugin_id not in vuln_host_map:

                v = copy.deepcopy(models.vulnerability_model)
                v['cves'] = list()
                v['plugin_ids'] = list()
                v['identified_by'] = list()
                v['hosts'] = list()

                # Set the title
                v['title'] = title

                # Set the description
                description = item.find('description')
                if description is not None:
                    v['description'] = description.text

                # Set the solution
                solution = item.find('solution')
                if solution is not None:
                    v['solution'] = solution.text

                # Set the evidence
                if evidence is not None:
                    v['evidence'] = evidence.text

                # Set the vulnerability flag if exploit exists
                exploit = item.find('exploit_available')
                if exploit is not None:
                    v['flag'] = exploit.text == 'true'

                    # Grab Metasploit details
                    exploit_detail = item.find('exploit_framework_metasploit')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Metasploit Exploit'
                        note_dict['content'] = 'Exploit exists. Details unknown.'
                        module = item.find('metasploit_name')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab Canvas details
                    exploit_detail = item.find('exploit_framework_canvas')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Canvas Exploit'
                        note_dict['content'] = 'Exploit exists. Details unknown.'
                        module = item.find('canvas_package')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab Core Impact details
                    exploit_detail = item.find('exploit_framework_core')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Core Impact Exploit'
                        note_dict['content'] = 'Exploit exists. Details unknown.'
                        module = item.find('core_name')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab ExploitHub SKUs
                    exploit_detail = item.find('exploit_framework_exploithub')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Exploit Hub Exploit'
                        note_dict['content'] = 'Exploit exists. Details unknown.'
                        module = item.find('exploithub_sku')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab any and all ExploitDB IDs
                    details = item.iter('edb-id')
                    if details is not None:
                        for module in details:
                            note_dict = copy.deepcopy(models.note_model)
                            note_dict['title'] = 'Exploit-DB Exploit ' \
                                                 '({0})'.format(module.text)
                            note_dict['content'] = module.text
                            note_dict['last_modified_by'] = TOOL
                            v['notes'].append(note_dict)

                # Set the CVSS score
                cvss = item.find('cvss_base_score')
                if cvss is not None:
                    v['cvss'] = float(cvss.text)
                else:
                    risk_factor = item.find('risk_factor')
                    if risk_factor is not None:
                        rf = risk_factor.text
                        if rf == "Low":
                            v['cvss'] = 3.0
                        elif rf == "Medium":
                            v['cvss'] = 5.0
                        elif rf == "High":
                            v['cvss'] = 7.5
                        elif rf == "Critical":
                            v['cvss'] = 10.0

                # Set the CVE(s)
                for cve in item.findall('cve'):
                    c = cve_pattern.sub('', cve.text)
                    v['cves'].append(c)

                # Set the plugin information
                plugin_dict = dict(models.plugin_id_model)
                plugin_dict['tool'] = TOOL
                plugin_dict['id'] = plugin_id
                v['plugin_ids'].append(plugin_dict)

                # Set the identified by information
                identified_dict = dict(models.identified_by_model)
                identified_dict['tool'] = TOOL
                identified_dict['id'] = plugin_id
                v['identified_by'].append(identified_dict)

                # By default, don't include informational findings unless
                # explicitly told to do so.
                if v['cvss'] == 0 and not include_informational:
                    continue

                vuln_host_map[plugin_id] = dict()
                vuln_host_map[plugin_id]['hosts'] = set()
                vuln_host_map[plugin_id]['vuln'] = v

            if plugin_id in vuln_host_map:
                vuln_host_map[plugin_id]['hosts'].add(
                    "{0}:{1}:{2}".format(
                        host_dict['string_addr'],
                        str(port),
                        protocol
                    )
                )

        # In the event no IP was found, use the 'name' attribute of
        # the 'ReportHost' element
        if not host_dict['string_addr']:
            host_dict['string_addr'] = temp_ip
            host_dict['long_addr'] = helper.ip2long(temp_ip)

        # Add all encountered ports to the host
        host_dict['ports'].extend(ports_processed.values())

        project_dict['hosts'].append(host_dict)

    # This code block uses the plugin/host/vuln mapping to associate
    # all vulnerable hosts to their vulnerability data within the
    # context of the expected Hive schema structure.
    for plugin_id, data in vuln_host_map.items():

        # Build list of host and ports affected by vulnerability and
        # assign that list to the vulnerability model
        for key in data['hosts']:
            (string_addr, port, protocol) = key.split(':')

            host_key_dict = dict(models.host_key_model)
            host_key_dict['string_addr'] = string_addr
            host_key_dict['port'] = int(port)
            host_key_dict['protocol'] = protocol
            data['vuln']['hosts'].append(host_key_dict)

        project_dict['vulnerabilities'].append(data['vuln'])

    if not project_dict['commands']:
        # Adds a dummy 'command' in the event the the Nessus plugin used
        # to populate the data was not run. The Lair API expects it to
        # contain a value.
        command = copy.deepcopy(models.command_model)
        command['tool'] = TOOL
        command['command'] = "Nessus scan - command unknown"
        project_dict['commands'].append(command)

    return project_dict
Пример #2
0
def parse(project, nessus_file, include_informational=False, min_note_sev=2):
    """Parses a Nessus XMLv2 file and updates the Hive database

    :param project: The project id
    :param nessus_file: The Nessus xml file to be parsed
    :param include_informational: Whether to include info findings in data. Default False
    :min_note_sev: The minimum severity of notes that will be saved. Default 2
    """

    cve_pattern = re.compile(r'(CVE-|CAN-)')
    false_udp_pattern = re.compile(r'.*\?$')

    tree = et.parse(nessus_file)
    root = tree.getroot()
    note_id = 1

    # Create the project dictionary which acts as foundation of document
    project_dict = dict(models.project_model)
    project_dict['commands'] = list()
    project_dict['vulnerabilities'] = list()
    project_dict['project_id'] = project

    # Used to maintain a running list of host:port vulnerabilities by plugin
    vuln_host_map = dict()

    for host in root.iter('ReportHost'):

        temp_ip = host.attrib['name']

        host_dict = dict(models.host_model)
        host_dict['os'] = list()
        host_dict['ports'] = list()
        host_dict['hostnames'] = list()

        # Tags contain host-specific information
        for tag in host.iter('tag'):

            # Operating system tag
            if tag.attrib['name'] == 'operating-system':
                os_dict = dict(models.os_model)
                os_dict['tool'] = TOOL
                os_dict['weight'] = OS_WEIGHT
                os_dict['fingerprint'] = tag.text
                host_dict['os'].append(os_dict)

            # IP address tag
            if tag.attrib['name'] == 'host-ip':
                host_dict['string_addr'] = tag.text
                host_dict['long_addr'] = helper.ip2long(tag.text)

            # MAC address tag
            if tag.attrib['name'] == 'mac-address':
                host_dict['mac_addr'] = tag.text

            # Hostname tag
            if tag.attrib['name'] == 'host-fqdn':
                host_dict['hostnames'].append(tag.text)

            # NetBIOS name tag
            if tag.attrib['name'] == 'netbios-name':
                host_dict['hostnames'].append(tag.text)

        # Track the unique port/protocol combos for a host so we don't
        # add duplicate entries
        ports_processed = dict()

        # Process each 'ReportItem'
        for item in host.findall('ReportItem'):
            plugin_id = item.attrib['pluginID']
            plugin_family = item.attrib['pluginFamily']
            severity = int(item.attrib['severity'])
            title = item.attrib['pluginName']

            port = int(item.attrib['port'])
            protocol = item.attrib['protocol']
            service = item.attrib['svc_name']
            evidence = item.find('plugin_output')

            # Ignore false positive UDP services
            if protocol == "udp" and false_udp_pattern.match(service):
                continue

            # Create a port model and temporarily store it in the dict
            # for tracking purposes. The ports_processed dict is used
            # later to add ports to the host so that no duplicates are
            # present. This is necessary due to the format of the Nessus
            # XML files.
            if '{0}:{1}'.format(port, protocol) not in ports_processed:
                port_dict = copy.deepcopy(models.port_model)
                port_dict['port'] = port
                port_dict['protocol'] = protocol
                port_dict['service'] = service
                ports_processed['{0}:{1}'.format(port, protocol)] = port_dict

            # Set the evidence as a port note if it exists
            if evidence is not None and \
                    severity >= min_note_sev and \
                    plugin_family != 'Port scanners' and \
                    plugin_family != 'Service detection':
                note_dict = copy.deepcopy(models.note_model)
                note_dict['title'] = "{0} (ID{1})".format(title, str(note_id))
                e = evidence.text.strip()
                for line in e.split("\n"):
                    line = line.strip()
                    if line:
                        note_dict['content'] += "    " + line + "\n"
                note_dict['last_modified_by'] = TOOL
                ports_processed['{0}:{1}'.format(
                    port, protocol)]['notes'].append(note_dict)
                note_id += 1

            # This plugin is general scan info...use it for 'command' element
            if plugin_id == '19506':

                command = item.find('plugin_output')

                command_dict = dict(models.command_model)
                command_dict['tool'] = TOOL

                if command is not None:
                    command_dict['command'] = command.text

                if not project_dict['commands']:
                    project_dict['commands'].append(command_dict)

                continue

            # Check if this vulnerability has been seen in this file for
            # another host. If not, create a new vulnerability_model and
            # maintain a mapping between plugin-id and vulnerability as
            # well as a mapping between plugin-id and host. These mappings
            # are later used to completed the Hive schema such that host
            # IP and port information are embedded within each vulnerability
            # while ensuring no duplicate data exists.
            if plugin_id not in vuln_host_map:

                v = copy.deepcopy(models.vulnerability_model)
                v['cves'] = list()
                v['plugin_ids'] = list()
                v['identified_by'] = list()
                v['hosts'] = list()

                # Set the title
                v['title'] = title

                # Set the description
                description = item.find('description')
                if description is not None:
                    v['description'] = description.text

                # Set the solution
                solution = item.find('solution')
                if solution is not None:
                    v['solution'] = solution.text

                # Set the evidence
                if evidence is not None:
                    v['evidence'] = evidence.text

                # Set the vulnerability flag if exploit exists
                exploit = item.find('exploit_available')
                if exploit is not None:
                    v['flag'] = exploit.text == 'true'

                    # Grab Metasploit details
                    exploit_detail = item.find('exploit_framework_metasploit')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Metasploit Exploit'
                        note_dict[
                            'content'] = 'Exploit exists. Details unknown.'
                        module = item.find('metasploit_name')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab Canvas details
                    exploit_detail = item.find('exploit_framework_canvas')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Canvas Exploit'
                        note_dict[
                            'content'] = 'Exploit exists. Details unknown.'
                        module = item.find('canvas_package')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab Core Impact details
                    exploit_detail = item.find('exploit_framework_core')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Core Impact Exploit'
                        note_dict[
                            'content'] = 'Exploit exists. Details unknown.'
                        module = item.find('core_name')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab ExploitHub SKUs
                    exploit_detail = item.find('exploit_framework_exploithub')
                    if exploit_detail is not None and \
                            exploit_detail.text == 'true':
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = 'Exploit Hub Exploit'
                        note_dict[
                            'content'] = 'Exploit exists. Details unknown.'
                        module = item.find('exploithub_sku')
                        if module is not None:
                            note_dict['content'] = module.text
                        note_dict['last_modified_by'] = TOOL
                        v['notes'].append(note_dict)

                    # Grab any and all ExploitDB IDs
                    details = item.iter('edb-id')
                    if details is not None:
                        for module in details:
                            note_dict = copy.deepcopy(models.note_model)
                            note_dict['title'] = 'Exploit-DB Exploit ' \
                                                 '({0})'.format(module.text)
                            note_dict['content'] = module.text
                            note_dict['last_modified_by'] = TOOL
                            v['notes'].append(note_dict)

                # Set the CVSS score
                cvss = item.find('cvss_base_score')
                if cvss is not None:
                    v['cvss'] = float(cvss.text)
                else:
                    risk_factor = item.find('risk_factor')
                    if risk_factor is not None:
                        rf = risk_factor.text
                        if rf == "Low":
                            v['cvss'] = 3.0
                        elif rf == "Medium":
                            v['cvss'] = 5.0
                        elif rf == "High":
                            v['cvss'] = 7.5
                        elif rf == "Critical":
                            v['cvss'] = 10.0

                # Set the CVE(s)
                for cve in item.findall('cve'):
                    c = cve_pattern.sub('', cve.text)
                    v['cves'].append(c)

                # Set the plugin information
                plugin_dict = dict(models.plugin_id_model)
                plugin_dict['tool'] = TOOL
                plugin_dict['id'] = plugin_id
                v['plugin_ids'].append(plugin_dict)

                # Set the identified by information
                identified_dict = dict(models.identified_by_model)
                identified_dict['tool'] = TOOL
                identified_dict['id'] = plugin_id
                v['identified_by'].append(identified_dict)

                # By default, don't include informational findings unless
                # explicitly told to do so.
                if v['cvss'] == 0 and not include_informational:
                    continue

                vuln_host_map[plugin_id] = dict()
                vuln_host_map[plugin_id]['hosts'] = set()
                vuln_host_map[plugin_id]['vuln'] = v

            if plugin_id in vuln_host_map:
                vuln_host_map[plugin_id]['hosts'].add("{0}:{1}:{2}".format(
                    host_dict['string_addr'], str(port), protocol))

        # In the event no IP was found, use the 'name' attribute of
        # the 'ReportHost' element
        if not host_dict['string_addr']:
            host_dict['string_addr'] = temp_ip
            host_dict['long_addr'] = helper.ip2long(temp_ip)

        # Add all encountered ports to the host
        host_dict['ports'].extend(ports_processed.values())

        project_dict['hosts'].append(host_dict)

    # This code block uses the plugin/host/vuln mapping to associate
    # all vulnerable hosts to their vulnerability data within the
    # context of the expected Hive schema structure.
    for plugin_id, data in vuln_host_map.items():

        # Build list of host and ports affected by vulnerability and
        # assign that list to the vulnerability model
        for key in data['hosts']:
            (string_addr, port, protocol) = key.split(':')

            host_key_dict = dict(models.host_key_model)
            host_key_dict['string_addr'] = string_addr
            host_key_dict['port'] = int(port)
            host_key_dict['protocol'] = protocol
            data['vuln']['hosts'].append(host_key_dict)

        project_dict['vulnerabilities'].append(data['vuln'])

    if not project_dict['commands']:
        # Adds a dummy 'command' in the event the the Nessus plugin used
        # to populate the data was not run. The Lair API expects it to
        # contain a value.
        command = copy.deepcopy(models.command_model)
        command['tool'] = TOOL
        command['command'] = "Nessus scan - command unknown"
        project_dict['commands'].append(command)

    return project_dict
Пример #3
0
def parse(project, nexpose_file, include_informational=False):
    """Parses a Nexpose XMLv2 file and updates the Lair database

    :param project: The project id
    :param nexpose_file: The Nexpose xml file to be parsed
    :include_informational: Whether to include info findings in data. Default False
    """

    cve_pattern = re.compile(r'(CVE-|CAN-)')
    html_tag_pattern = re.compile(r'<.*?>')
    white_space_pattern = re.compile(r'\s+', re.MULTILINE)

    # Used to create unique notes in DB
    note_id = 1

    tree = et.parse(nexpose_file)
    root = tree.getroot()
    if root is None or \
            root.tag != "NexposeReport" or \
            root.attrib['version'] != "2.0":
        raise IncompatibleDataVersionError("Nexpose XML 2.0")

    # Create the project dictionary which acts as foundation of document
    project_dict = dict(models.project_model)
    project_dict['commands'] = list()
    project_dict['vulnerabilities'] = list()
    project_dict['project_id'] = project
    project_dict['commands'].append({'tool': TOOL, 'command': 'scan'})

    # Used to maintain a running list of host:port vulnerabilities by plugin
    vuln_host_map = dict()

    for vuln in root.iter('vulnerability'):
        v = copy.deepcopy(models.vulnerability_model)
        v['cves'] = list()
        v['plugin_ids'] = list()
        v['identified_by'] = list()
        v['hosts'] = list()

        v['cvss'] = float(vuln.attrib['cvssScore'])
        v['title'] = vuln.attrib['title']
        plugin_id = vuln.attrib['id'].lower()

        # Set plugin id
        plugin_dict = dict(models.plugin_id_model)
        plugin_dict['tool'] = TOOL
        plugin_dict['id'] = plugin_id
        v['plugin_ids'].append(plugin_dict)

        # Set identified by information
        identified_dict = dict(models.identified_by_model)
        identified_dict['tool'] = TOOL
        identified_dict['id'] = plugin_id
        v['identified_by'].append(identified_dict)

        # Search for exploits
        for exploit in vuln.iter('exploit'):
            v['flag'] = True
            note_dict = copy.deepcopy(models.note_model)
            note_dict['title'] = "{0} ({1})".format(
                exploit.attrib['type'],
                exploit.attrib['id']
            )
            note_dict['content'] = "{0}\n{1}".format(
                exploit.attrib['title'].encode('ascii', 'replace'),
                exploit.attrib['link'].encode('ascii', 'replace')
            )
            note_dict['last_modified_by'] = TOOL
            v['notes'].append(note_dict)

        # Search for CVE references
        for reference in vuln.iter('reference'):
            if reference.attrib['source'] == 'CVE':
                cve = cve_pattern.sub('', reference.text)
                v['cves'].append(cve)

        # Search for solution
        solution = vuln.find('solution')
        if solution is not None:
            for text in solution.itertext():
                s = text.encode('ascii', 'replace').strip()
                v['solution'] += white_space_pattern.sub(" ", s)

        # Search for description
        description = vuln.find('description')
        if description is not None:
            for text in description.itertext():
                s = text.encode('ascii', 'replace').strip()
                v['description'] += white_space_pattern.sub(" ", s)

        # Build mapping of plugin-id to host to vuln dictionary
        vuln_host_map[plugin_id] = dict()
        vuln_host_map[plugin_id]['vuln'] = v
        vuln_host_map[plugin_id]['hosts'] = set()

    for node in root.iter('node'):

        host_dict = dict(models.host_model)
        host_dict['os'] = list()
        host_dict['ports'] = list()
        host_dict['hostnames'] = list()

        # Set host status
        if node.attrib['status'] != 'alive':
            host_dict['alive'] = False

        # Set IP address
        host_dict['string_addr'] = node.attrib['address']
        host_dict['long_addr'] = helper.ip2long(node.attrib['address'])

        # Set the OS fingerprint
        certainty = 0
        for os in node.iter('os'):
            if float(os.attrib['certainty']) > certainty:
                certainty = float(os.attrib['certainty'])
                os_dict = dict(models.os_model)
                os_dict['tool'] = TOOL
                os_dict['weight'] = OS_WEIGHT

                fingerprint = ''
                if 'vendor' in os.attrib:
                    fingerprint += os.attrib['vendor'] + " "

                # Make an extra check to limit duplication of data in the
                # event that the product name was already in the vendor name
                if 'product' in os.attrib and \
                        os.attrib['product'] not in fingerprint:
                    fingerprint += os.attrib['product'] + " "

                fingerprint = fingerprint.strip()
                os_dict['fingerprint'] = fingerprint

                host_dict['os'] = list()
                host_dict['os'].append(os_dict)

        # Test for general, non-port related vulnerabilities
        # Add them as tcp, port 0
        tests = node.find('tests')
        if tests is not None:
            port_dict = dict(models.port_model)
            port_dict['service'] = "general"

            for test in tests.findall('test'):
                # vulnerable-since attribute is used to flag
                # confirmed vulns
                if 'vulnerable-since' in test.attrib:
                    plugin_id = test.attrib['id'].lower()

                    # This is used to track evidence for the host/port
                    # and plugin
                    h = "{0}:{1}:{2}".format(
                        host_dict['string_addr'],
                        "0",
                        models.PROTOCOL_TCP
                    )
                    vuln_host_map[plugin_id]['hosts'].add(h)

            host_dict['ports'].append(port_dict)

        # Use the endpoint elements to populate port data
        for endpoint in node.iter('endpoint'):
            port_dict = copy.deepcopy(models.port_model)
            port_dict['port'] = int(endpoint.attrib['port'])
            port_dict['protocol'] = endpoint.attrib['protocol']
            if endpoint.attrib['status'] != 'open':
                port_dict['alive'] = False

            # Use the service elements to identify service
            for service in endpoint.iter('service'):

                # Ignore unknown services
                if 'unknown' not in service.attrib['name'].lower():
                    if not port_dict['service']:
                        port_dict['service'] = service.attrib['name'].lower()

                # Use the test elements to identify vulnerabilities for
                # the host
                for test in service.iter('test'):
                    # vulnerable-since attribute is used to flag
                    # confirmed vulns
                    if 'vulnerable-since' in test.attrib:
                        plugin_id = test.attrib['id'].lower()

                        # Add service notes for evidence
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = "{0} (ID{1})".format(plugin_id,
                                                              str(note_id))
                        for evidence in test.iter():
                            if evidence.text:
                                for line in evidence.text.split("\n"):
                                    line = line.strip()
                                    if line:
                                        note_dict['content'] += "    " + \
                                                                line + "\n"
                            elif evidence.tag == "URLLink":
                                note_dict['content'] += "    "
                                note_dict['content'] += evidence.attrib[
                                                            'LinkURL'
                                                        ] + "\n"

                        note_dict['last_modified_by'] = TOOL
                        port_dict['notes'].append(note_dict)
                        note_id += 1

                        # This is used to track evidence for the host/port
                        # and plugin
                        h = "{0}:{1}:{2}".format(
                            host_dict['string_addr'],
                            str(port_dict['port']),
                            port_dict['protocol']
                        )
                        vuln_host_map[plugin_id]['hosts'].add(h)

            # Use the fingerprint elements to identify product
            certainty = 0
            for fingerprint in endpoint.iter('fingerprint'):
                if float(fingerprint.attrib['certainty']) > certainty:
                    certainty = float(fingerprint.attrib['certainty'])
                    prod = ''
                    if 'vendor' in fingerprint.attrib:
                        prod += fingerprint.attrib['vendor'] + " "

                    if 'product' in fingerprint.attrib:
                        prod += fingerprint.attrib['product'] + " "

                    if 'version' in fingerprint.attrib:
                        prod += fingerprint.attrib['version'] + " "

                    prod = prod.strip()
                    port_dict['product'] = prod

            host_dict['ports'].append(port_dict)

        project_dict['hosts'].append(host_dict)

    # This code block uses the plugin/host/vuln mapping to associate
    # all vulnerable hosts to their vulnerability data within the
    # context of the expected Lair schema structure.
    for plugin_id, data in vuln_host_map.items():

        # Build list of host and ports affected by vulnerability and
        # assign that list to the vulnerability model
        for key in data['hosts']:
            (string_addr, port, protocol) = key.split(':')

            host_key_dict = dict(models.host_key_model)
            host_key_dict['string_addr'] = string_addr
            host_key_dict['port'] = int(port)
            host_key_dict['protocol'] = protocol
            data['vuln']['hosts'].append(host_key_dict)

        # By default, don't include informational findings unless
        # explicitly told to do so.
        if data['vuln']['cvss'] == 0 and not include_informational:
            continue

        project_dict['vulnerabilities'].append(data['vuln'])

    return project_dict
Пример #4
0
def parse_xml(project, resource):
    """Parses an Nmap XML file and updates the Lair database

    :param project: The project id
    :param resource: The Nmap xml file or xml string to be parsed
    """

    # Attempt to parse resource as file or string
    try:
        if os.path.isfile(resource):
            tree = et.parse(resource)
            root = tree.getroot()
        else:
            root = et.fromstring(resource)
    except et.ParseError:
        raise

    # Create the project dictionary which acts as foundation of document
    project_dict = copy.deepcopy(models.project_model)
    project_dict['project_id'] = project

    # Pull the command from the file
    command_dict = copy.deepcopy(models.command_model)
    command_dict['tool'] = TOOL

    if root.tag == 'nmaprun':
        command_dict['command'] = root.attrib['args']
    else:
        command = root.find('nmaprun')
        if command is not None:
            command_dict['command'] = command.attrib['args']

    project_dict['commands'].append(command_dict)

    # Process each 'host' in the file
    for host in root.findall('host'):

        host_dict = copy.deepcopy(models.host_model)

        # Find the host status
        status = host.find('status')
        if status is not None:
            if status.attrib['state'] != 'up':
                host_dict['alive'] = False

        if status is None or not host_dict.get('alive', False):
            # Don't import dead hosts
            continue

        # Find the IP address and/or MAC address
        for addr in host.findall('address'):

            # Get IP address
            if addr.attrib['addrtype'] == 'ipv4':
                host_dict['string_addr'] = addr.attrib['addr']
                host_dict['long_addr'] = helper.ip2long(addr.attrib['addr'])
            elif addr.attrib['addrtype'] == 'mac':
                host_dict['mac_addr'] = addr.attrib['addr']

        # Find the host names
        for hostname in host.iter('hostname'):
            host_dict['hostnames'].append(hostname.attrib['name'])

        # Find the ports
        for port in host.iter('port'):
            port_dict = copy.deepcopy(models.port_model)
            port_dict['port'] = int(port.attrib['portid'])
            port_dict['protocol'] = port.attrib['protocol']

            # Find port status
            status = port.find('state')
            if status is not None:
                if status.attrib['state'] != 'open':
                    continue
                port_dict['alive'] = True

            # Find port service and product
            service = port.find('service')
            if service is not None:
                port_dict['service'] = service.attrib['name']
                if 'product' in service.attrib:
                    if 'version' in service.attrib:
                        port_dict['product'] = service.attrib['product'] + " " + service.attrib['version']
                    else:
                        port_dict['product'] = service.attrib['product']
                else:
                    port_dict['product'] = "unknown"

            # Find NSE script output
            for script in port.findall('script'):
                note_dict = copy.deepcopy(models.note_model)
                note_dict['title'] = script.attrib['id']
                note_dict['content'] = script.attrib['output']
                note_dict['last_modified_by'] = TOOL
                port_dict['notes'].append(note_dict)

            host_dict['ports'].append(port_dict)

        # Find the Operating System
        os_dict = copy.deepcopy(models.os_model)
        os_dict['tool'] = TOOL
        os_list = list(host.iter('osmatch'))
        if os_list:
            os_dict['weight'] = OS_WEIGHT
            os_dict['fingerprint'] = os_list[0].attrib['name']

        host_dict['os'].append(os_dict)

        project_dict['hosts'].append(host_dict)

    return project_dict
def parse(project, nexpose_file, include_informational=False):
    """Parses a Nexpose XMLv2 file and updates the Lair database

    :param project: The project id
    :param nexpose_file: The Nexpose xml file to be parsed
    :include_informational: Whether to include info findings in data. Default False
    """

    cve_pattern = re.compile(r'(CVE-|CAN-)')
    html_tag_pattern = re.compile(r'<.*?>')
    white_space_pattern = re.compile(r'\s+', re.MULTILINE)

    # Used to create unique notes in DB
    note_id = 1

    tree = et.parse(nexpose_file)
    root = tree.getroot()
    if root is None or \
            root.tag != "NexposeReport" or \
            root.attrib['version'] != "2.0":
        raise IncompatibleDataVersionError("Nexpose XML 2.0")

    # Create the project dictionary which acts as foundation of document
    project_dict = dict(models.project_model)
    project_dict['commands'] = list()
    project_dict['vulnerabilities'] = list()
    project_dict['project_id'] = project
    project_dict['commands'].append({'tool': TOOL, 'command': 'scan'})

    # Used to maintain a running list of host:port vulnerabilities by plugin
    vuln_host_map = dict()

    for vuln in root.iter('vulnerability'):
        v = copy.deepcopy(models.vulnerability_model)
        v['cves'] = list()
        v['plugin_ids'] = list()
        v['identified_by'] = list()
        v['hosts'] = list()

        v['cvss'] = float(vuln.attrib['cvssScore'])
        v['title'] = vuln.attrib['title']
        plugin_id = vuln.attrib['id'].lower()

        # Set plugin id
        plugin_dict = dict(models.plugin_id_model)
        plugin_dict['tool'] = TOOL
        plugin_dict['id'] = plugin_id
        v['plugin_ids'].append(plugin_dict)

        # Set identified by information
        identified_dict = dict(models.identified_by_model)
        identified_dict['tool'] = TOOL
        identified_dict['id'] = plugin_id
        v['identified_by'].append(identified_dict)

        # Search for exploits
        for exploit in vuln.iter('exploit'):
            v['flag'] = True
            note_dict = copy.deepcopy(models.note_model)
            note_dict['title'] = "{0} ({1})".format(exploit.attrib['type'],
                                                    exploit.attrib['id'])
            note_dict['content'] = "{0}\n{1}".format(
                exploit.attrib['title'].encode('ascii', 'replace'),
                exploit.attrib['link'].encode('ascii', 'replace'))
            note_dict['last_modified_by'] = TOOL
            v['notes'].append(note_dict)

        # Search for CVE references
        for reference in vuln.iter('reference'):
            if reference.attrib['source'] == 'CVE':
                cve = cve_pattern.sub('', reference.text)
                v['cves'].append(cve)

        # Search for solution
        solution = vuln.find('solution')
        if solution is not None:
            for text in solution.itertext():
                s = text.encode('ascii', 'replace').strip()
                v['solution'] += white_space_pattern.sub(" ", s)

        # Search for description
        description = vuln.find('description')
        if description is not None:
            for text in description.itertext():
                s = text.encode('ascii', 'replace').strip()
                v['description'] += white_space_pattern.sub(" ", s)

        # Build mapping of plugin-id to host to vuln dictionary
        vuln_host_map[plugin_id] = dict()
        vuln_host_map[plugin_id]['vuln'] = v
        vuln_host_map[plugin_id]['hosts'] = set()

    for node in root.iter('node'):

        host_dict = dict(models.host_model)
        host_dict['os'] = list()
        host_dict['ports'] = list()
        host_dict['hostnames'] = list()

        # Set host status
        if node.attrib['status'] != 'alive':
            host_dict['alive'] = False

        # Set IP address
        host_dict['string_addr'] = node.attrib['address']
        host_dict['long_addr'] = helper.ip2long(node.attrib['address'])

        # Set the OS fingerprint
        certainty = 0
        for os in node.iter('os'):
            if float(os.attrib['certainty']) > certainty:
                certainty = float(os.attrib['certainty'])
                os_dict = dict(models.os_model)
                os_dict['tool'] = TOOL
                os_dict['weight'] = OS_WEIGHT

                fingerprint = ''
                if 'vendor' in os.attrib:
                    fingerprint += os.attrib['vendor'] + " "

                # Make an extra check to limit duplication of data in the
                # event that the product name was already in the vendor name
                if 'product' in os.attrib and \
                        os.attrib['product'] not in fingerprint:
                    fingerprint += os.attrib['product'] + " "

                fingerprint = fingerprint.strip()
                os_dict['fingerprint'] = fingerprint

                host_dict['os'] = list()
                host_dict['os'].append(os_dict)

        # Test for general, non-port related vulnerabilities
        # Add them as tcp, port 0
        tests = node.find('tests')
        if tests is not None:
            port_dict = dict(models.port_model)
            port_dict['service'] = "general"

            for test in tests.findall('test'):
                # vulnerable-since attribute is used to flag
                # confirmed vulns
                if 'vulnerable-since' in test.attrib:
                    plugin_id = test.attrib['id'].lower()

                    # This is used to track evidence for the host/port
                    # and plugin
                    h = "{0}:{1}:{2}".format(host_dict['string_addr'], "0",
                                             models.PROTOCOL_TCP)
                    vuln_host_map[plugin_id]['hosts'].add(h)

            host_dict['ports'].append(port_dict)

        # Use the endpoint elements to populate port data
        for endpoint in node.iter('endpoint'):
            port_dict = copy.deepcopy(models.port_model)
            port_dict['port'] = int(endpoint.attrib['port'])
            port_dict['protocol'] = endpoint.attrib['protocol']
            if endpoint.attrib['status'] != 'open':
                port_dict['alive'] = False

            # Use the service elements to identify service
            for service in endpoint.iter('service'):

                # Ignore unknown services
                if 'unknown' not in service.attrib['name'].lower():
                    if not port_dict['service']:
                        port_dict['service'] = service.attrib['name'].lower()

                # Use the test elements to identify vulnerabilities for
                # the host
                for test in service.iter('test'):
                    # vulnerable-since attribute is used to flag
                    # confirmed vulns
                    if 'vulnerable-since' in test.attrib:
                        plugin_id = test.attrib['id'].lower()

                        # Add service notes for evidence
                        note_dict = copy.deepcopy(models.note_model)
                        note_dict['title'] = "{0} (ID{1})".format(
                            plugin_id, str(note_id))
                        for evidence in test.iter():
                            if evidence.text:
                                for line in evidence.text.split("\n"):
                                    line = line.strip()
                                    if line:
                                        note_dict['content'] += "    " + \
                                                                line + "\n"
                            elif evidence.tag == "URLLink":
                                note_dict['content'] += "    "
                                note_dict['content'] += evidence.attrib[
                                    'LinkURL'] + "\n"

                        note_dict['last_modified_by'] = TOOL
                        port_dict['notes'].append(note_dict)
                        note_id += 1

                        # This is used to track evidence for the host/port
                        # and plugin
                        h = "{0}:{1}:{2}".format(host_dict['string_addr'],
                                                 str(port_dict['port']),
                                                 port_dict['protocol'])
                        vuln_host_map[plugin_id]['hosts'].add(h)

            # Use the fingerprint elements to identify product
            certainty = 0
            for fingerprint in endpoint.iter('fingerprint'):
                if float(fingerprint.attrib['certainty']) > certainty:
                    certainty = float(fingerprint.attrib['certainty'])
                    prod = ''
                    if 'vendor' in fingerprint.attrib:
                        prod += fingerprint.attrib['vendor'] + " "

                    if 'product' in fingerprint.attrib:
                        prod += fingerprint.attrib['product'] + " "

                    if 'version' in fingerprint.attrib:
                        prod += fingerprint.attrib['version'] + " "

                    prod = prod.strip()
                    port_dict['product'] = prod

            host_dict['ports'].append(port_dict)

        project_dict['hosts'].append(host_dict)

    # This code block uses the plugin/host/vuln mapping to associate
    # all vulnerable hosts to their vulnerability data within the
    # context of the expected Lair schema structure.
    for plugin_id, data in vuln_host_map.items():

        # Build list of host and ports affected by vulnerability and
        # assign that list to the vulnerability model
        for key in data['hosts']:
            (string_addr, port, protocol) = key.split(':')

            host_key_dict = dict(models.host_key_model)
            host_key_dict['string_addr'] = string_addr
            host_key_dict['port'] = int(port)
            host_key_dict['protocol'] = protocol
            data['vuln']['hosts'].append(host_key_dict)

        # By default, don't include informational findings unless
        # explicitly told to do so.
        if data['vuln']['cvss'] == 0 and not include_informational:
            continue

        project_dict['vulnerabilities'].append(data['vuln'])

    return project_dict
Пример #6
0
def parse(project, resource):
    """Parses an Nmap XML file and updates the Lair database

    :param project: The project id
    :param resource: The Nmap xml file or xml string to be parsed
    """

    # Attempt to parse resource as file or string
    try:
        if os.path.isfile(resource):
            tree = et.parse(resource)
            root = tree.getroot()
        else:
            root = et.fromstring(resource)
    except et.ParseError:
        raise

    # Create the project dictionary which acts as foundation of document
    project_dict = copy.deepcopy(models.project_model)
    project_dict['project_id'] = project

    # Pull the command from the file
    command_dict = copy.deepcopy(models.command_model)
    command_dict['tool'] = TOOL

    if root.tag == 'nmaprun':
        command_dict['command'] = root.attrib['args']
    else:
        command = root.find('nmaprun')
        if command is not None:
            command_dict['command'] = command.attrib['args']

    project_dict['commands'].append(command_dict)

    # Process each 'host' in the file
    for host in root.findall('host'):

        host_dict = copy.deepcopy(models.host_model)

        # Find the host status
        status = host.find('status')
        if status is not None:
            if status.attrib['state'] != 'up':
                host_dict['alive'] = False

        # Find the IP address and/or MAC address
        for addr in host.findall('address'):

            # Get IP address
            if addr.attrib['addrtype'] == 'ipv4':
                host_dict['string_addr'] = addr.attrib['addr']
                host_dict['long_addr'] = helper.ip2long(addr.attrib['addr'])
            elif addr.attrib['addrtype'] == 'mac':
                host_dict['mac_addr'] = addr.attrib['addr']

        # Find the host names
        for hostname in host.iter('hostname'):
            host_dict['hostnames'].append(hostname.attrib['name'])

        # Find the ports
        for port in host.iter('port'):
            port_dict = copy.deepcopy(models.port_model)
            port_dict['port'] = int(port.attrib['portid'])
            port_dict['protocol'] = port.attrib['protocol']

            # Find port status
            status = port.find('state')
            if status is not None:
                if status.attrib['state'] != 'open':
                    continue
                port_dict['alive'] = True

            # Find port service and product
            service = port.find('service')
            if service is not None:
                port_dict['service'] = service.attrib['name']
                if 'product' in service.attrib:
                    if 'version' in service.attrib:
                        port_dict['product'] = service.attrib['product'] + " " + service.attrib['version']
                    else:    
                        port_dict['product'] = service.attrib['product']
                else:
                    port_dict['product'] = "unknown"

            # Find NSE script output
            for script in port.findall('script'):
                note_dict = copy.deepcopy(models.note_model)
                note_dict['title'] = script.attrib['id']
                note_dict['content'] = script.attrib['output']
                note_dict['last_modified_by'] = TOOL
                port_dict['notes'].append(note_dict)

            host_dict['ports'].append(port_dict)

        # Find the Operating System
        os_dict = copy.deepcopy(models.os_model)
        os_dict['tool'] = TOOL
        os_list = list(host.iter('osmatch'))
        if os_list:
            os_dict['weight'] = OS_WEIGHT
            os_dict['fingerprint'] = os_list[0].attrib['name']

        host_dict['os'].append(os_dict)

        project_dict['hosts'].append(host_dict)

    return project_dict
Пример #7
0
def parse_xml(project, resource):
    """Parses an Nmap XML file and updates the Lair database

    :param project: The project id
    :param resource: The Nmap xml file or xml string to be parsed
    """

    # Attempt to parse resource as file or string
    try:
        if os.path.isfile(resource):
            tree = et.parse(resource)
            root = tree.getroot()
        else:
            root = et.fromstring(resource)
    except et.ParseError:
        raise

    # Create the project dictionary which acts as foundation of document
    project_dict = copy.deepcopy(models.project_model)
    project_dict["project_id"] = project

    # Pull the command from the file
    command_dict = copy.deepcopy(models.command_model)
    command_dict["tool"] = TOOL

    if root.tag == "nmaprun":
        command_dict["command"] = root.attrib["args"]
    else:
        command = root.find("nmaprun")
        if command is not None:
            command_dict["command"] = command.attrib["args"]

    project_dict["commands"].append(command_dict)

    # Process each 'host' in the file
    for host in root.findall("host"):

        host_dict = copy.deepcopy(models.host_model)

        # Find the host status
        status = host.find("status")
        if status is not None:
            if status.attrib["state"] != "up":
                host_dict["alive"] = False

        if status is None or not host_dict.get("alive", False):
            # Don't import dead hosts
            continue

        # Find the IP address and/or MAC address
        for addr in host.findall("address"):

            # Get IP address
            if addr.attrib["addrtype"] == "ipv4":
                host_dict["string_addr"] = addr.attrib["addr"]
                host_dict["long_addr"] = helper.ip2long(addr.attrib["addr"])
            elif addr.attrib["addrtype"] == "mac":
                host_dict["mac_addr"] = addr.attrib["addr"]

        # Find the host names
        for hostname in host.iter("hostname"):
            host_dict["hostnames"].append(hostname.attrib["name"])

        # Find the ports
        for port in host.iter("port"):
            port_dict = copy.deepcopy(models.port_model)
            port_dict["port"] = int(port.attrib["portid"])
            port_dict["protocol"] = port.attrib["protocol"]

            # Find port status
            status = port.find("state")
            if status is not None:
                if status.attrib["state"] != "open":
                    continue
                port_dict["alive"] = True

            # Find port service and product
            service = port.find("service")
            if service is not None:
                port_dict["service"] = service.attrib["name"]
                if "product" in service.attrib:
                    if "version" in service.attrib:
                        port_dict["product"] = service.attrib["product"] + " " + service.attrib["version"]
                    else:
                        port_dict["product"] = service.attrib["product"]
                else:
                    port_dict["product"] = "unknown"

            # Find NSE script output
            for script in port.findall("script"):
                note_dict = copy.deepcopy(models.note_model)
                note_dict["title"] = script.attrib["id"]
                note_dict["content"] = script.attrib["output"]
                note_dict["last_modified_by"] = TOOL
                port_dict["notes"].append(note_dict)

            host_dict["ports"].append(port_dict)

        # Find the Operating System
        os_dict = copy.deepcopy(models.os_model)
        os_dict["tool"] = TOOL
        os_list = list(host.iter("osmatch"))
        if os_list:
            os_dict["weight"] = OS_WEIGHT
            os_dict["fingerprint"] = os_list[0].attrib["name"]

        host_dict["os"].append(os_dict)

        project_dict["hosts"].append(host_dict)

    return project_dict