Esempio n. 1
0
def process_xml(
    filename=None,
    addnoports=False,
    asset_group=None,
    engineer=None,
    msf_settings={},
    ip_ignore_list=None,
    ip_include_list=None,
    update_hosts=False,
):
    # Upload and process Nmap XML Scan file
    import re
    import os
    from skaldship.general import get_host_record, do_host_status
    from skaldship.cpe import lookup_cpe
    from zenmapCore_Kvasir.NmapParser import NmapParser

    # output regexes
    RE_NETBIOS_NAME = re.compile("NetBIOS computer name: (?P<d>.*),")
    RE_NETBIOS_WORKGROUP = re.compile("Workgroup: (?P<d>.*),")
    RE_NETBIOS_MAC = re.compile("NetBIOS MAC: (?P<d>([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}))")

    # build the hosts only/exclude list
    ip_exclude = []
    if ip_ignore_list:
        ip_exclude = ip_ignore_list.split("\r\n")
        # TODO: check for ip subnet/range and break it out to individuals
    ip_only = []
    if ip_include_list:
        ip_only = ip_include_list.split("\r\n")
        # TODO: check for ip subnet/range and break it out to individuals

    log(" [*] Processing Nmap scan file %s" % (filename))

    nmap_parsed = NmapParser()
    nmap_parsed.parse_file(filename)

    # existing_vulnids = db(db.t_vulndata()).select(db.t_vulndata.id, db.t_vulndata.f_vulnid).as_dict(key='f_vulnid')

    # parse the hosts, where all the goodies are
    log(" [-] Parsing %d hosts" % (len(nmap_parsed.hosts)))
    hoststats = {}
    hoststats["added"] = 0
    hoststats["skipped"] = 0
    hoststats["updated"] = 0
    hoststats["errored"] = 0
    hosts = []  # array of host_id fields

    svc_db = db.t_services
    for node in nmap_parsed.hosts:
        nodefields = {}

        if node.ipv6:
            ipaddr = node.ipv6
            nodefields["f_ipv4"] = ipaddr
        elif node.ip.get("type") == "ipv4":
            ipaddr = node.ip.get("addr")
            nodefields["f_ipv4"] = ipaddr
        else:
            log(" [!] No IPv4/IPv6 address, skipping")
            continue

        try:
            nodefields["f_macaddr"] = node.mac["addr"]
        except TypeError:
            nodefields["f_macaddr"] = None

        status = node.state

        log(" [-] Host %s status is: %s" % (ipaddr, status))
        if status != "up":
            hoststats["skipped"] += 1
            continue

        if ipaddr in ip_exclude:
            log(" [-] Host is in exclude list... skipping")
            hoststats["skipped"] += 1
            continue

        if len(ip_only) > 0 and ipaddr not in ip_only:
            log(" [-] Host is not in the only list... skipping")
            hoststats["skipped"] += 1
            continue

        if not node.ports and not addnoports:
            log(" [-] No ports open and not asked to add those kind... skipping")
            hoststats["skipped"] += 1
            continue

        # we'lll just take the last hostname in the names list since it'll usually be the full dns name
        for name in node.hostnames:
            nodefields["f_hostname"] = name["hostname"]

        nodefields["f_engineer"] = engineer
        nodefields["f_asset_group"] = asset_group
        nodefields["f_confirmed"] = False

        # see if host exists, if so update. if not, insert!
        query = (db.t_hosts.f_ipv4 == ipaddr) | (db.t_hosts.f_ipv6 == ipaddr)
        host_rec = db(query).select().first()

        if host_rec is None:
            host_id = db.t_hosts.insert(**nodefields)
            db.commit()
            hoststats["added"] += 1
            log(" [-] Adding %s" % (ipaddr))
        elif host_rec is not None and update_hosts:
            db.commit()
            if "f_ipv4" in nodefields:
                host_id = db(db.t_hosts.f_ipv4 == nodefields["f_ipv4"]).update(**nodefields)
            else:
                host_id = db(db.t_hosts.f_ipv6 == nodefields["f_ipv6"]).update(**nodefields)
            db.commit()
            host_id = get_host_record(ipaddr)
            host_id = host_id.id
            hoststats["updated"] += 1
            log(" [-] Updating %s" % (ipaddr))
        else:
            hoststats["skipped"] += 1
            db.commit()
            log(" [-] Skipped %s" % (ipaddr))
            continue
        hosts.append(host_id)

        # process OS related info
        for os in node.osmatches:
            os_id = None
            host_id = None
            f_title = os["name"]  # title
            for k in os["osclasses"]:
                f_cpename = k["cpe"].lstrip("cpe:/o:")
                f_vendor = k["vendor"]
                f_product = k["osfamily"]
                f_version = k["osgen"]
                f_class = k["type"]
                f_family = k["osfamily"]
                f_certainty = k["accuracy"]

                cpe_res = db((db.t_os.f_cpename == f_cpename) & (db.t_os.f_title == f_title)).select().first()

                if cpe_res is not None:
                    os_id = cpe_res.id

                else:
                    try:
                        os_id = db.t_os.insert(
                            f_cpename=f_cpename,
                            f_title=f_title,
                            f_vendor=f_vendor,
                            f_product=f_product,
                            f_version=f_version,
                        )
                    except Exception, e:
                        logger.error("Error inserting OS: %s" % (e))

                    db.commit()

                if os_id and (f_class or f_family or f_certainty):
                    ipaddr = node.ip.get("addr")
                    host_id = get_host_record(ipaddr)
                    host_id = host_id.id
                    try:
                        db.t_host_os_refs.insert(
                            f_certainty=f_certainty,
                            f_family=f_family,
                            f_class=f_class,
                            f_hosts_id=host_id,
                            f_os_id=os_id,
                        )
                    except Exception, e:
                        logger.error("Error inserting OS: %s" % (e))
                    db.commit()
Esempio n. 2
0
def process_xml(
    filename=None,
    addnoports=False,
    asset_group=None,
    engineer=None,
    msf_workspace=False,
    ip_ignore_list=None,
    ip_include_list=None,
    update_hosts=False,
    ):
    # Upload and process nMap XML Scan file
    import re
    from MetasploitAPI import MetasploitAPI
    from skaldship.general import get_host_record, do_host_status
    from skaldship.cpe import lookup_cpe
    from zenmapCore_Kvasir.NmapParser import NmapParser

    # output regexes
    RE_NETBIOS_NAME = re.compile('NetBIOS computer name: (?P<d>.*),')
    RE_NETBIOS_WORKGROUP = re.compile('Workgroup: (?P<d>.*),')
    RE_NETBIOS_MAC = re.compile('NetBIOS MAC: (?P<d>([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}))')

    # build the hosts only/exclude list
    ip_exclude = []
    if ip_ignore_list:
        ip_exclude = ip_ignore_list.split('\r\n')
        # TODO: check for ip subnet/range and break it out to individuals
    ip_only = []
    if ip_include_list:
        ip_only = ip_include_list.split('\r\n')
        # TODO: check for ip subnet/range and break it out to individuals

    log(" [*] Processing nMap scan file %s" % (filename))

    nmap_parsed = NmapParser()
    nmap_parsed.parse_file(filename)

    #existing_vulnids = db(db.t_vulndata()).select(db.t_vulndata.id, db.t_vulndata.f_vulnid).as_dict(key='f_vulnid')

    user_id = db.auth_user(engineer) or auth.user.id

    # parse the hosts, where all the goodies are
    log(" [-] Parsing %d hosts" % (len(nmap_parsed.hosts)))
    hoststats = {}
    hoststats['added'] = 0
    hoststats['skipped'] = 0
    hoststats['updated'] = 0
    hoststats['errored'] = 0
    hosts = []   # array of host_id fields

    svc_db = db.t_services
    for node in nmap_parsed.hosts:
        nodefields = {}

        if node.ipv6:
            ipaddr = node.ipv6
            nodefields['f_ipv4'] = ipaddr
        elif node.ip.get('type') == 'ipv4':
            ipaddr = node.ip.get('addr')
            nodefields['f_ipv4'] = ipaddr
        else:
            log(" [!] No IPv4/IPv6 address, skipping")
            continue

        nodefields['f_macaddr'] = node.mac
        status = node.state

        log(" [-] Host %s status is: %s" % (ipaddr, status))
        if status != "up":
            hoststats['skipped'] += 1
            continue

        if ipaddr in ip_exclude:
            log(" [-] Host is in exclude list... skipping")
            hoststats['skipped'] += 1
            continue

        if len(ip_only) > 0 and ipaddr not in ip_only:
            log(" [-] Host is not in the only list... skipping")
            hoststats['skipped'] += 1
            continue

        if not node.ports and not addnoports:
            log(" [-] No ports open and not asked to add those kind... skipping")
            hoststats['skipped'] += 1
            continue

        # we'lll just take the last hostname in the names list since it'll usually be the full dns name
        for name in node.hostnames:
            nodefields['f_hostname'] = name

        nodefields['f_engineer'] = user_id
        nodefields['f_asset_group'] = asset_group
        nodefields['f_confirmed'] = False

        # check to see if IPv4/IPv6 exists in DB already
        if 'f_ipv4' in nodefields:
            host_rec = db(db.t_hosts.f_ipv4 == nodefields['f_ipv4']).select().first()
        elif 'f_ipv6' in nodefields:
            host_rec = db(db.t_hosts.f_ipv6 == nodefields['f_ipv6']).select().first()
        else:
            log("No IP Address found in record. Skipping", logging.ERROR)
            continue

        if host_rec is None:
            host_id = db.t_hosts.insert(**nodefields)
            db.commit()
            hoststats['added'] += 1
            log(" [-] Adding %s" % (ipaddr))
        elif host_rec is not None and update_hosts:
            db.commit()
            if 'f_ipv4' in nodefields:
                host_id = db(db.t_hosts.f_ipv4 == nodefields['f_ipv4']).update(**nodefields)
            else:
                host_id = db(db.t_hosts.f_ipv6 == nodefields['f_ipv6']).update(**nodefields)
            db.commit()
            host_id = get_host_record(ipaddr)
            host_id = host_id.id
            hoststats['updated'] += 1
            log(" [-] Updating %s" % (ipaddr))
        else:
            hoststats['skipped'] += 1
            db.commit()
            log(" [-] Skipped %s" % (ipaddr))
            continue
        hosts.append(host_id)

        # process non-port <hostscript> entries. Add to info/0:
        for hostscripts in node.hostscripts:
            query = (svc_db.f_proto == 'info') & (svc_db.f_number == 0) & (svc_db.f_hosts_id == host_id)
            svc_id = db.t_services.update_or_insert(query, f_proto='info', f_number=0, f_status='open', f_hosts_id=host_id)
            if not svc_id:
                svc_rec = db(query).select(cache=(cache.ram, 180)).first()
                if svc_rec:
                    svc_id = svc_rec.id
                else:
                    log(" [!] Service record wasn't created", logging.ERROR)
                    continue

            db.commit()
            for script in hostscripts:
                script_id = script.id
                output = script.output
                db.t_service_info.update_or_insert(f_services_id=svc_id, f_name=script_id, f_text=output)
                db.commit()

                if script_id == 'nbstat':
                    # pull out NetBIOS info from nbstat output
                    result = RE_NETBIOS_MAC.search(output)
                    if 'd' in result.groupdict():
                        host_rec.update(f_macaddr=result.group('d'))
                        db.commit()
                    result = RE_NETBIOS_NAME.search(output)
                    if 'd' in result.groupdict():
                        host_rec.update(f_netbios_name=result.group('d'))
                        db.commit()
                    result = RE_NETBIOS_WORKGROUP.search(output)
                    if 'd' in result.groupdict():
                        db(db.t_netbios.update_or_insert(f_hosts_id=host_id, f_domain=result.group('d')))
                        db.commit()

        # add ports and resulting vulndata
        for port in node.ports:
            f_proto = port.get('protocol')
            f_number = port.get('portid')
            f_status = port.get('port_state')
            f_name = port.get('service_name')
            f_product = port.get('service_product')

            log(" [-] Adding port: %s/%s (%s)" % (f_proto, f_number, f_name))
            svc_id = db.t_services.update_or_insert(f_proto=f_proto, f_number=f_number, f_status=f_status, f_hosts_id=host_id, f_name=f_name)

            if f_product:
                version = port.get('service_version')
                if version:
                    f_product += " (%s)" % (version)
                db.t_service_info.update_or_insert(f_services_id=svc_id, f_name=f_name, f_text=f_product)
                db.commit()

            # Process <script> service entries
            for script in port.get('scripts'):
                db.t_service_info.update_or_insert(f_services_id=svc_id, f_name=script.get('id'), f_text=script.get('output'))
                db.commit()

            # Process <cpe> service entries
            for port_cpe in port.get('service_cpe'):
                cpe_id = port_cpe.text.lstrip('cpe:/')

                if cpe_id[0] == "a":
                    # process CPE Applications
                    #log(" [-] Found Application CPE data: %s" % (cpe_id))
                    db.t_service_info.update_or_insert(f_services_id=svc_id, f_name='cpe.app', f_text="cpe:/%s" % (cpe_id))
                    db.commit()

                elif cpe_id[0] == "o":
                    # process CPE Operating System

                    os_id = lookup_cpe(cpe_id[2:])

                    if os_id is not None:
                        db.t_host_os_refs.insert(f_certainty='0.9',
                                                 f_family='Unknown',
                                                 f_class='Other',
                                                 f_hosts_id=host_id,
                                                 f_os_id=os_id)
                        db.commit()
                    else:
                        # So no CPE or existing OS data, lets split up the CPE data and make our own
                        log(" [!] No os_id found, this is odd !!!")

    if msf_workspace:
        msf = MetasploitAPI(host=user_id.f_msf_pro_url, apikey=user_id.f_msf_pro_key)
        if msf.login():
            try:
                res = msf.pro_import_file(
                    msf_workspace,
                    filename,
                    {
                        'DS_REMOVE_FILE': False,
                        'tag': asset_group,
                        },
                )
                log(" [*] Added file to MSF Pro: %s" % (res))
            except MetasploitAPI.MSFAPIError, e:
                logging.error("MSFAPI Error: %s" % (e))
                pass
        else:
            log(" [!] Unable to login to Metasploit PRO, check your API key", logging.ERROR)
            msf = None
Esempio n. 3
0
def process_xml(
    filename=None,
    addnoports=False,
    asset_group=None,
    engineer=None,
    msf_settings={},
    ip_ignore_list=None,
    ip_include_list=None,
    update_hosts=False,
):
    # Upload and process Nmap XML Scan file
    import re
    import os
    from skaldship.general import get_host_record, do_host_status
    from skaldship.cpe import lookup_cpe
    from zenmapCore_Kvasir.NmapParser import NmapParser

    # output regexes
    RE_NETBIOS_NAME = re.compile("NetBIOS computer name: (?P<d>.*),")
    RE_NETBIOS_WORKGROUP = re.compile("Workgroup: (?P<d>.*),")
    RE_NETBIOS_MAC = re.compile("NetBIOS MAC: (?P<d>([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}))")

    # build the hosts only/exclude list
    ip_exclude = []
    if ip_ignore_list:
        ip_exclude = ip_ignore_list.split("\r\n")
        # TODO: check for ip subnet/range and break it out to individuals
    ip_only = []
    if ip_include_list:
        ip_only = ip_include_list.split("\r\n")
        # TODO: check for ip subnet/range and break it out to individuals

    log(" [*] Processing Nmap scan file %s" % (filename))

    nmap_parsed = NmapParser()
    nmap_parsed.parse_file(filename)

    # existing_vulnids = db(db.t_vulndata()).select(db.t_vulndata.id, db.t_vulndata.f_vulnid).as_dict(key='f_vulnid')

    # parse the hosts, where all the goodies are
    log(" [-] Parsing %d hosts" % (len(nmap_parsed.hosts)))
    hoststats = {}
    hoststats["added"] = 0
    hoststats["skipped"] = 0
    hoststats["updated"] = 0
    hoststats["errored"] = 0
    hosts = []  # array of host_id fields

    svc_db = db.t_services
    for node in nmap_parsed.hosts:
        nodefields = {}

        if node.ipv6:
            ipaddr = node.ipv6
            nodefields["f_ipv4"] = ipaddr
        elif node.ip.get("type") == "ipv4":
            ipaddr = node.ip.get("addr")
            nodefields["f_ipv4"] = ipaddr
        else:
            log(" [!] No IPv4/IPv6 address, skipping")
            continue

        try:
            nodefields["f_macaddr"] = node.mac["addr"]
        except TypeError:
            nodefields["f_macaddr"] = None

        status = node.state

        log(" [-] Host %s status is: %s" % (ipaddr, status))
        if status != "up":
            hoststats["skipped"] += 1
            continue

        if ipaddr in ip_exclude:
            log(" [-] Host is in exclude list... skipping")
            hoststats["skipped"] += 1
            continue

        if len(ip_only) > 0 and ipaddr not in ip_only:
            log(" [-] Host is not in the only list... skipping")
            hoststats["skipped"] += 1
            continue

        if not node.ports and not addnoports:
            log(" [-] No ports open and not asked to add those kind... skipping")
            hoststats["skipped"] += 1
            continue

        # we'lll just take the last hostname in the names list since it'll usually be the full dns name
        for name in node.hostnames:
            nodefields["f_hostname"] = name["hostname"]

        nodefields["f_engineer"] = engineer
        nodefields["f_asset_group"] = asset_group
        nodefields["f_confirmed"] = False

        # see if host exists, if so update. if not, insert!
        query = (db.t_hosts.f_ipv4 == ipaddr) | (db.t_hosts.f_ipv6 == ipaddr)
        host_rec = db(query).select().first()

        if host_rec is None:
            host_id = db.t_hosts.insert(**nodefields)
            db.commit()
            hoststats["added"] += 1
            log(" [-] Adding %s" % (ipaddr))
        elif host_rec is not None and update_hosts:
            db.commit()
            if "f_ipv4" in nodefields:
                host_id = db(db.t_hosts.f_ipv4 == nodefields["f_ipv4"]).update(**nodefields)
            else:
                host_id = db(db.t_hosts.f_ipv6 == nodefields["f_ipv6"]).update(**nodefields)
            db.commit()
            host_id = get_host_record(ipaddr)
            host_id = host_id.id
            hoststats["updated"] += 1
            log(" [-] Updating %s" % (ipaddr))
        else:
            hoststats["skipped"] += 1
            db.commit()
            log(" [-] Skipped %s" % (ipaddr))
            continue
        hosts.append(host_id)

        # process non-port <hostscript> entries. Add to info/0:
        for hostscripts in node.hostscripts:
            query = (svc_db.f_proto == "info") & (svc_db.f_number == 0) & (svc_db.f_hosts_id == host_id)
            svc_id = db.t_services.update_or_insert(
                query, f_proto="info", f_number=0, f_status="open", f_hosts_id=host_id
            )
            if not svc_id:
                svc_rec = db(query).select(cache=(cache.ram, 180)).first()
                if svc_rec:
                    svc_id = svc_rec.id
                else:
                    log(" [!] Service record wasn't created", logging.ERROR)
                    continue

            db.commit()
            for script in hostscripts:
                script_id = script.id
                output = script.output
                db.t_service_info.update_or_insert(f_services_id=svc_id, f_name=script_id, f_text=output)
                db.commit()

                if script_id == "nbstat":
                    # pull out NetBIOS info from nbstat output
                    result = RE_NETBIOS_MAC.search(output)
                    if "d" in result.groupdict():
                        host_rec.update(f_macaddr=result.group("d"))
                        db.commit()
                    result = RE_NETBIOS_NAME.search(output)
                    if "d" in result.groupdict():
                        host_rec.update(f_netbios_name=result.group("d"))
                        db.commit()
                    result = RE_NETBIOS_WORKGROUP.search(output)
                    if "d" in result.groupdict():
                        db(db.t_netbios.update_or_insert(f_hosts_id=host_id, f_domain=result.group("d")))
                        db.commit()

        # add ports and resulting vulndata
        for port in node.ports:
            f_proto = port.get("protocol")
            f_number = port.get("portid")
            f_status = port.get("port_state")
            f_name = port.get("service_name")
            f_product = port.get("service_product")

            log(" [-] Adding port: %s/%s (%s)" % (f_proto, f_number, f_name))
            svc_id = db.t_services.update_or_insert(
                f_proto=f_proto, f_number=f_number, f_status=f_status, f_hosts_id=host_id, f_name=f_name
            )

            if f_product:
                version = port.get("service_version")
                if version:
                    f_product += " (%s)" % (version)
                db.t_service_info.update_or_insert(f_services_id=svc_id, f_name=f_name, f_text=f_product)
                db.commit()

            # Process <script> service entries
            for script in port.get("scripts"):
                db.t_service_info.update_or_insert(
                    f_services_id=svc_id, f_name=script.get("id"), f_text=script.get("output")
                )
                db.commit()

            # Process <cpe> service entries
            port_cpe = port.get("service_cpe")
            if port_cpe:
                cpe_id = port_cpe.lstrip("cpe:/")

                if cpe_id.startswith("a"):
                    # process CPE Applications
                    # log(" [-] Found Application CPE data: %s" % (cpe_id))
                    db.t_service_info.update_or_insert(
                        f_services_id=svc_id, f_name="cpe.app", f_text="cpe:/%s" % (cpe_id)
                    )
                    db.commit()

                elif cpe_id.startswith("o"):
                    # process CPE Operating System

                    os_id = lookup_cpe(cpe_id[2:])

                    if os_id is not None:
                        db.t_host_os_refs.insert(
                            f_certainty="0.9", f_family="Unknown", f_class="Other", f_hosts_id=host_id, f_os_id=os_id
                        )
                        db.commit()
                    else:
                        # So no CPE or existing OS data, lets split up the CPE data and make our own
                        log(" [!] No os_id found, this is odd !!!")

    if msf_settings.get("workspace"):
        try:
            # check to see if we have a Metasploit RPC instance configured and talking
            from MetasploitAPI import MetasploitAPI

            msf_api = MetasploitAPI(host=msf_settings.get("url"), apikey=msf_settings.get("key"))
            working_msf_api = msf_api.login()
        except Exception, error:
            log(" [!] Unable to authenticate to MSF API: %s" % str(error), logging.ERROR)
            working_msf_api = False

        try:
            scan_data = open(filename, "r+").readlines()
        except Exception, error:
            log(" [!] Error loading scan data to send to Metasploit: %s" % str(error), logging.ERROR)
            scan_data = None
Esempio n. 4
0
def process_xml(
    filename=None,
    addnoports=False,
    asset_group=None,
    engineer=None,
    msf_settings={},
    ip_ignore_list=None,
    ip_include_list=None,
    update_hosts=False,
):
    # Upload and process Nmap XML Scan file
    import re
    import os
    from skaldship.hosts import get_host_record, do_host_status
    from skaldship.cpe import lookup_cpe
    from zenmapCore_Kvasir.NmapParser import NmapParser
    from gluon import current
    T = current.T

    # output regexes
    RE_NETBIOS_NAME = re.compile('NetBIOS computer name: (?P<d>.*),')
    RE_NETBIOS_WORKGROUP = re.compile('Workgroup: (?P<d>.*),')
    RE_NETBIOS_MAC = re.compile(
        'NetBIOS MAC: (?P<d>([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}))')

    # build the hosts only/exclude list
    ip_exclude = []
    if ip_ignore_list:
        ip_exclude = ip_ignore_list.split('\r\n')
        # TODO: check for ip subnet/range and break it out to individuals
    ip_only = []
    if ip_include_list:
        ip_only = ip_include_list.split('\r\n')
        # TODO: check for ip subnet/range and break it out to individuals

    log(" [*] Processing Nmap scan file %s" % (filename))

    nmap_parsed = NmapParser()
    nmap_parsed.parse_file(filename)

    #existing_vulnids = db(db.t_vulndata()).select(db.t_vulndata.id, db.t_vulndata.f_vulnid).as_dict(key='f_vulnid')

    # parse the hosts, where all the goodies are
    log(" [-] Parsing %d hosts" % (len(nmap_parsed.hosts)))
    hoststats = {}
    hoststats['added'] = 0
    hoststats['skipped'] = 0
    hoststats['updated'] = 0
    hoststats['errored'] = 0
    hosts = []  # array of host_id fields

    svc_db = db.t_services
    for node in nmap_parsed.hosts:
        nodefields = {}

        if node.ipv6:
            ipaddr = node.ipv6
            nodefields['f_ipaddr'] = ipaddr
        elif node.ip.get('type') == 'ipv4':
            ipaddr = node.ip.get('addr')
            nodefields['f_ipaddr'] = ipaddr
        else:
            log(" [!] No IPv4/IPv6 address, skipping")
            continue

        try:
            nodefields['f_macaddr'] = node.mac['addr']
        except TypeError:
            nodefields['f_macaddr'] = None

        status = node.state

        log(" [-] Host %s status is: %s" % (ipaddr, status))
        if status != "up":
            hoststats['skipped'] += 1
            continue

        if ipaddr in ip_exclude:
            log(" [-] Host is in exclude list... skipping")
            hoststats['skipped'] += 1
            continue

        if len(ip_only) > 0 and ipaddr not in ip_only:
            log(" [-] Host is not in the only list... skipping")
            hoststats['skipped'] += 1
            continue

        if not node.ports and not addnoports:
            log(" [-] No ports open and not asked to add those kind... skipping"
                )
            hoststats['skipped'] += 1
            continue

        # we'll just take the last hostname in the names list since it'll usually be the full dns name
        for name in node.hostnames:
            nodefields['f_hostname'] = name['hostname']

        nodefields['f_engineer'] = engineer
        nodefields['f_asset_group'] = asset_group
        nodefields['f_confirmed'] = False

        # see if host exists, if so update. if not, insert!
        query = (db.t_hosts.f_ipaddr == ipaddr)
        host_rec = db(query).select().first()

        if host_rec is None:
            host_id = db.t_hosts.insert(**nodefields)
            db.commit()
            hoststats['added'] += 1
            log(" [-] Adding %s" % (ipaddr))
        elif host_rec is not None and update_hosts:
            db.commit()
            host_id = db(db.t_hosts.f_ipaddr == nodefields['f_ipaddr']).update(
                **nodefields)
            db.commit()
            host_id = get_host_record(ipaddr)
            host_id = host_id.id
            hoststats['updated'] += 1
            log(" [-] Updating %s" % (ipaddr))
        else:
            hoststats['skipped'] += 1
            db.commit()
            log(" [-] Skipped %s" % (ipaddr))
            continue
        hosts.append(host_id)

        # process OS related info
        for os in node.osmatches:
            os_id = None
            host_id = None
            f_title = os['name']  #title
            for k in os['osclasses']:
                if k.get('cpe') != None:  #fixes error on loading cpe:/os
                    f_cpename = k['cpe'].replace('cpe:/o:', '')
                    f_vendor = k['vendor']
                    f_product = k['osfamily']
                    f_version = k['osgen']
                    f_class = k['type']
                    f_family = k['osfamily']
                    f_certainty = k['accuracy']

                    cpe_res = db((db.t_os.f_cpename == f_cpename) & (
                        db.t_os.f_title == f_title)).select().first()

                    if cpe_res is not None:
                        os_id = cpe_res.id

                    else:
                        try:
                            os_id = db.t_os.insert(
                                f_cpename=f_cpename,
                                f_title=f_title,
                                f_vendor=f_vendor,
                                f_product=f_product,
                                f_version=f_version,
                            )
                        except Exception, e:
                            logger.error("Error inserting OS: %s" % (e))

                        db.commit()

                if os_id and (f_class or f_family or f_certainty):
                    ipaddr = node.ip.get('addr')
                    host_id = get_host_record(ipaddr)
                    host_id = host_id.id
                    try:
                        db.t_host_os_refs.insert(f_certainty=f_certainty,
                                                 f_family=f_family,
                                                 f_class=f_class,
                                                 f_hosts_id=host_id,
                                                 f_os_id=os_id)
                    except Exception, e:
                        logger.error("Error inserting OS: %s" % (e))
                    db.commit()
Esempio n. 5
0
def process_xml(
    filename=None,
    addnoports=False,
    asset_group=None,
    engineer=None,
    msf_settings={},
    ip_ignore_list=None,
    ip_include_list=None,
    update_hosts=False,
):
    # Upload and process Nmap XML Scan file
    import re
    import os
    from skaldship.hosts import get_host_record, do_host_status
    from skaldship.cpe import lookup_cpe
    from zenmapCore_Kvasir.NmapParser import NmapParser
    from gluon import current
    T = current.T

    # output regexes
    RE_NETBIOS_NAME = re.compile('NetBIOS computer name: (?P<d>.*),')
    RE_NETBIOS_WORKGROUP = re.compile('Workgroup: (?P<d>.*),')
    RE_NETBIOS_MAC = re.compile(
        'NetBIOS MAC: (?P<d>([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2}))')

    # build the hosts only/exclude list
    ip_exclude = []
    if ip_ignore_list:
        ip_exclude = ip_ignore_list.split('\r\n')
        # TODO: check for ip subnet/range and break it out to individuals
    ip_only = []
    if ip_include_list:
        ip_only = ip_include_list.split('\r\n')
        # TODO: check for ip subnet/range and break it out to individuals

    log(" [*] Processing Nmap scan file %s" % (filename))

    nmap_parsed = NmapParser()
    nmap_parsed.parse_file(filename)

    #existing_vulnids = db(db.t_vulndata()).select(db.t_vulndata.id, db.t_vulndata.f_vulnid).as_dict(key='f_vulnid')

    # parse the hosts, where all the goodies are
    log(" [-] Parsing %d hosts" % (len(nmap_parsed.hosts)))
    hoststats = {}
    hoststats['added'] = 0
    hoststats['skipped'] = 0
    hoststats['updated'] = 0
    hoststats['errored'] = 0
    hosts = []  # array of host_id fields

    svc_db = db.t_services
    for node in nmap_parsed.hosts:
        nodefields = {}

        if node.ipv6:
            ipaddr = node.ipv6
            nodefields['f_ipaddr'] = ipaddr
        elif node.ip.get('type') == 'ipv4':
            ipaddr = node.ip.get('addr')
            nodefields['f_ipaddr'] = ipaddr
        else:
            log(" [!] No IPv4/IPv6 address, skipping")
            continue

        try:
            nodefields['f_macaddr'] = node.mac['addr']
        except TypeError:
            nodefields['f_macaddr'] = None

        status = node.state

        log(" [-] Host %s status is: %s" % (ipaddr, status))
        if status != "up":
            hoststats['skipped'] += 1
            continue

        if ipaddr in ip_exclude:
            log(" [-] Host is in exclude list... skipping")
            hoststats['skipped'] += 1
            continue

        if len(ip_only) > 0 and ipaddr not in ip_only:
            log(" [-] Host is not in the only list... skipping")
            hoststats['skipped'] += 1
            continue

        if not node.ports and not addnoports:
            log(" [-] No ports open and not asked to add those kind... skipping"
                )
            hoststats['skipped'] += 1
            continue

        # we'll just take the last hostname in the names list since it'll usually be the full dns name
        for name in node.hostnames:
            nodefields['f_hostname'] = name['hostname']

        nodefields['f_engineer'] = engineer
        nodefields['f_asset_group'] = asset_group
        nodefields['f_confirmed'] = False

        # see if host exists, if so update. if not, insert!
        query = (db.t_hosts.f_ipaddr == ipaddr)
        host_rec = db(query).select().first()

        if host_rec is None:
            host_id = db.t_hosts.insert(**nodefields)
            db.commit()
            hoststats['added'] += 1
            log(" [-] Adding %s" % (ipaddr))
        elif host_rec is not None and update_hosts:
            db.commit()
            host_id = db(db.t_hosts.f_ipaddr == nodefields['f_ipaddr']).update(
                **nodefields)
            db.commit()
            host_id = get_host_record(ipaddr)
            host_id = host_id.id
            hoststats['updated'] += 1
            log(" [-] Updating %s" % (ipaddr))
        else:
            hoststats['skipped'] += 1
            db.commit()
            log(" [-] Skipped %s" % (ipaddr))
            continue
        hosts.append(host_id)

        # process OS related info
        for os in node.osmatches:
            os_id = None
            host_id = None
            f_title = os['name']  #title
            for k in os['osclasses']:
                if k.get('cpe') != None:  #fixes error on loading cpe:/os
                    f_cpename = k['cpe'].replace('cpe:/o:', '')
                    f_vendor = k['vendor']
                    f_product = k['osfamily']
                    f_version = k['osgen']
                    f_class = k['type']
                    f_family = k['osfamily']
                    f_certainty = k['accuracy']

                    cpe_res = db((db.t_os.f_cpename == f_cpename) & (
                        db.t_os.f_title == f_title)).select().first()

                    if cpe_res is not None:
                        os_id = cpe_res.id

                    else:
                        try:
                            os_id = db.t_os.insert(
                                f_cpename=f_cpename,
                                f_title=f_title,
                                f_vendor=f_vendor,
                                f_product=f_product,
                                f_version=f_version,
                            )
                        except Exception as e:
                            logger.error("Error inserting OS: %s" % (e))

                        db.commit()

                if os_id and (f_class or f_family or f_certainty):
                    ipaddr = node.ip.get('addr')
                    host_id = get_host_record(ipaddr)
                    host_id = host_id.id
                    try:
                        db.t_host_os_refs.insert(f_certainty=f_certainty,
                                                 f_family=f_family,
                                                 f_class=f_class,
                                                 f_hosts_id=host_id,
                                                 f_os_id=os_id)
                    except Exception as e:
                        logger.error("Error inserting OS: %s" % (e))
                    db.commit()

        # process non-port <hostscript> entries. Add to info/0:
        for hostscripts in node.hostscripts:
            query = (svc_db.f_proto == 'info') & (svc_db.f_number == 0) & (
                svc_db.f_hosts_id == host_id)
            svc_id = db.t_services.update_or_insert(query,
                                                    f_proto='info',
                                                    f_number=0,
                                                    f_status='open',
                                                    f_hosts_id=host_id)
            if not svc_id:
                svc_rec = db(query).select(cache=(cache.ram, 180)).first()
                if svc_rec:
                    svc_id = svc_rec.id
                else:
                    log(" [!] Service record wasn't created", logging.ERROR)
                    continue

            db.commit()
            for script in hostscripts:
                script_id = script.id
                output = script.output
                db.t_service_info.update_or_insert(f_services_id=svc_id,
                                                   f_name=script_id,
                                                   f_text=output)
                db.commit()

                if script_id == 'nbstat':
                    # pull out NetBIOS info from nbstat output
                    result = RE_NETBIOS_MAC.search(output)
                    if 'd' in result.groupdict():
                        host_rec.update(f_macaddr=result.group('d'))
                        db.commit()
                    result = RE_NETBIOS_NAME.search(output)
                    if 'd' in result.groupdict():
                        host_rec.update(f_netbios_name=result.group('d'))
                        db.commit()
                    result = RE_NETBIOS_WORKGROUP.search(output)
                    if 'd' in result.groupdict():
                        db(
                            db.t_netbios.update_or_insert(
                                f_hosts_id=host_id,
                                f_domain=result.group('d')))
                        db.commit()

        # add ports and resulting vulndata
        for port in node.ports:
            f_proto = port.get('protocol')
            f_number = port.get('portid')
            f_status = port.get('port_state')
            f_name = port.get('service_name')
            f_product = port.get('service_product')

            query = (svc_db.f_proto == f_proto) & (
                svc_db.f_number == f_number) & (svc_db.f_hosts_id == host_id)
            svc = db(query).select().first()

            # if record does not exist, query returns None so add the record:
            if svc is None:
                log(" [-] Adding port: %s/%s (%s)" %
                    (f_proto, f_number, f_name))
                svc_id = db.t_services.update_or_insert(f_proto=f_proto,
                                                        f_number=f_number,
                                                        f_status=f_status,
                                                        f_name=f_name,
                                                        f_hosts_id=host_id)

            if not (svc is None):
                log(" [-] Updating port: %s/%s (%s)" %
                    (f_proto, f_number, f_name))
                if svc.f_name != f_name and f_name not in svc.f_name:
                    svc_id = db.t_services.validate_and_update(
                        _key=svc.id, f_name=(f_name + ' | ' + svc.f_name))
                else:
                    svc_id = db.t_services.validate_and_update(
                        _key=svc.id, f_name=(svc.f_name))
                svc_id = svc_id.id

            if f_product:
                version = port.get('service_version')
                if version:
                    f_product += " (%s)" % (version)
                db.t_service_info.update_or_insert(f_services_id=svc_id,
                                                   f_name=f_name,
                                                   f_text=f_product)
                db.commit()

            # Process <script> service entries
            for script in port.get('scripts'):
                try:
                    if script.get('id') == 'ssl-cert':
                        text = script.get('output')
                        sslcert = text.replace("/", "\n")
                        db.t_service_info.update_or_insert(
                            f_services_id=svc_id,
                            f_name=script.get('id'),
                            f_text=sslcert)
                    else:
                        db.t_service_info.update_or_insert(
                            f_services_id=svc_id,
                            f_name=script.get('id'),
                            f_text=script.get('output'))
                except Exception as e:
                    logger.error("Error inserting Script: %s" % (e))
                db.commit()
                # check for banner id and update t_services banner field with the output
                if script.get('id') == "banner":
                    try:
                        db.t_services.update_or_insert(
                            (db.t_services.id == svc_id),
                            f_banner=script.get('output'))
                    except Exception as e:
                        logger.error("Error inserting Banner: %s" % (e))
                    db.commit()

            # Process <cpe> service entries
            port_cpe = port.get('service_cpe')
            if port_cpe:
                cpe_id = port_cpe.replace('cpe:/', '')

                if cpe_id.startswith('a'):
                    # process CPE Applications
                    #log(" [-] Found Application CPE data: %s" % (cpe_id))
                    db.t_service_info.update_or_insert(f_services_id=svc_id,
                                                       f_name='cpe.app',
                                                       f_text="cpe:/%s" %
                                                       (cpe_id))
                    db.commit()

                elif cpe_id.startswith('o'):
                    # process CPE Operating System

                    os_id = lookup_cpe(cpe_id[2:])

                    if os_id is not None:
                        db.t_host_os_refs.insert(f_certainty='0.9',
                                                 f_family='Unknown',
                                                 f_class='Other',
                                                 f_hosts_id=host_id,
                                                 f_os_id=os_id)
                        db.commit()
                    else:
                        # So no CPE or existing OS data, lets split up the CPE data and make our own
                        log(" [!] No os_id found, this is odd !!!")

        # Adding uptime. Needed to add a table "f_uptime" in t_hosts db!
        if node.uptime['lastboot']:
            db.t_hosts.update_or_insert((db.t_hosts.f_ipaddr == ipaddr),
                                        f_uptime=node.uptime['lastboot'])
        if not node.uptime['lastboot']:
            db.t_hosts.update_or_insert((db.t_hosts.f_ipaddr == ipaddr),
                                        f_uptime=T("no entry found"))

    if msf_settings.get('workspace'):
        try:
            # check to see if we have a Metasploit RPC instance configured and talking
            from MetasploitProAPI import MetasploitProAPI
            msf_api = MetasploitProAPI(host=msf_settings.get('url'),
                                       apikey=msf_settings.get('key'))
            working_msf_api = msf_api.login()
        except Exception as error:
            log(" [!] Unable to authenticate to MSF API: %s" % str(error),
                logging.ERROR)
            working_msf_api = False

        try:
            scan_data = open(filename, "r+").readlines()
        except Exception as error:
            log(
                " [!] Error loading scan data to send to Metasploit: %s" %
                str(error), logging.ERROR)
            scan_data = None

        if scan_data and working_msf_api:
            task = msf_api.pro_import_data(
                msf_settings.get('workspace'),
                "".join(scan_data),
                {
                    #'preserve_hosts': form.vars.preserve_hosts,
                    'blacklist_hosts': "\n".join(ip_ignore_list)
                },
            )

            msf_workspace_num = session.msf_workspace_num or 'unknown'
            msfurl = os.path.join(msf_settings.get('url'), 'workspaces',
                                  msf_workspace_num, 'tasks', task['task_id'])
            log(" [*] Added file to MSF Pro: %s" % msfurl)

    # any new nexpose vulns need to be checked against exploits table and connected
    log(" [*] Connecting exploits to vulns and performing do_host_status")
    do_host_status(asset_group=asset_group)

    log(" [*] Import complete: hosts: %s added, %s updated, %s skipped" % (
        hoststats['added'],
        hoststats['updated'],
        hoststats['skipped'],
    ))