def get_description(self, address, name, cpe): '''Define a description based on hostname and CPE''' if name: return name else: c = CPE(cpe[0], CPE.VERSION_2_3) vendor = c.get_vendor()[0].upper() if vendor in self.devs: try: client = SSHClient() client.set_missing_host_key_policy(AutoAddPolicy()) client.connect(address, username=self.devs[vendor]['USER'], password=self.devs[vendor]['PASSWORD']) stdin, stdout, stderr = client.exec_command( self.devs[vendor]['COMMAND']) return '{}:{}'.format( vendor.lower(), re.search(self.devs[vendor]['REGEX'], str(stdout.read().decode('utf-8'))).group( self.devs[vendor]['REGROUP'])) except (AuthenticationException, SSHException, NoValidConnectionsError, TimeoutError, ConnectionResetError): pass return '{}.{}.{}'.format(c.get_vendor()[0], c.get_product()[0], c.get_version()[0])
def get_description(self, address, name, cpe): '''Define a description based on hostname and CPE''' if name: return name else: c = CPE(cpe[0], CPE.VERSION_2_3) vendor = c.get_vendor()[0] if self.tacacs and vendor == 'cisco': try: client = SSHClient() client.set_missing_host_key_policy(AutoAddPolicy()) client.connect(address, username=self.tacacs['user'], password=self.tacacs['password']) stdin, stdout, stderr = client.exec_command( self.tacacs['command']) return '{}:{}'.format( vendor.lower(), re.search(self.tacacs['regex'], str(stdout.read().decode('utf-8'))).group( self.tacacs['regroup'])) except (AuthenticationException, SSHException, NoValidConnectionsError, TimeoutError, ConnectionResetError): pass return '{}.{}.{}'.format(c.get_vendor()[0], c.get_product()[0], c.get_version()[0])
def process_running_on_configuration(self, node): simple_cpes = defaultdict(lambda: defaultdict(set)) running_on = defaultdict(lambda: defaultdict(set)) if 'children' not in node: # TODO if this happens, we would need to add a constraint in FMR saying that X cpe needs Y cpe and viceversa. for complex_cpe in node['cpe_match']: aux = CPE(complex_cpe['cpe23Uri']) simple_cpes[aux.get_vendor()[0]][aux.get_product()[0]].update( self.expand(complex_cpe)) else: for subnode in node['children']: isVulnerable = subnode['cpe_match'][0]['vulnerable'] if isVulnerable: # simple cpe for complex_cpe in subnode['cpe_match']: aux = CPE(complex_cpe['cpe23Uri']) simple_cpes[aux.get_vendor()[0]][ aux.get_product()[0]].update( self.expand(complex_cpe)) else: # running on for complex_cpe in subnode['cpe_match']: aux = CPE(complex_cpe['cpe23Uri']) running_on[aux.get_vendor()[0]][ aux.get_product()[0]].update( self.expand(complex_cpe)) return simple_cpes, running_on
def process_basic_configuration(container: dict) -> dict: ''' Extracts, expands and conditionally expand the CPEs records of a Configuration Container of type BASIC. :param container: A valid container wrapping information about a configuration ''' # First, we need to check the arguments if not container or type(container) is not dict: raise ValueError("container must be a non-empty Parsed JSON Object") if container["configType"] != ConfigType.BASIC.name or container[ 'cpes'] is None: raise ValueError( "container must be a valid structure of type: {} ".format( ConfigType.BASIC.name)) res = defaultdict(lambda: defaultdict(set)) for complex_cpe in container['cpes']: aux = CPE(complex_cpe['cpe23Uri']) res[aux.get_vendor()[0]][aux.get_product()[0]].update( expand(complex_cpe)) return res
def process_basic_configuration(self, node): res = defaultdict(lambda: defaultdict(set)) for complex_cpe in node['cpe_match']: aux = CPE(complex_cpe['cpe23Uri']) res[aux.get_vendor()[0]][aux.get_product()[0]].update( self.expand(complex_cpe)) return res
def config_cpe_match(self, cm): if all("$.vulnerable", cm)[0]: v = PLATFORM.VulnerableConfiguration else: v = PLATFORM.NotVulnerableConfiguration subject = BNode() cveStr = all("$.cpe23Uri", cm)[0] self.triples(subject, v, [(PLATFORM.hasPlatform, cpeURI(cveStr))] + \ self.versionStartExcluding(cm) + self.versionStartIncluding(cm) + self.versionEndExcluding(cm) + self.versionEndIncluding(cm)) #print(cveStr) c = CPE(cveStr) if c.is_hardware(): self.g.add((cpeURI(cveStr), RDF.type, PLATFORM.HardwarePlatform)) elif c.is_application(): self.g.add( (cpeURI(cveStr), RDF.type, PLATFORM.ApplicationPlatform)) elif c.is_operating_system(): self.g.add( (cpeURI(cveStr), RDF.type, PLATFORM.OperatingSystemPlatform)) vendor = "" for i in c.get_vendor(): self.g.add((cpeURI(cveStr), PLATFORM.vendor, self.plEnt(i, "Vendor_", cls=PLATFORM.Vendor))) vendor = i for i in c.get_product(): self.g.add((cpeURI(cveStr), PLATFORM.product, self.plEnt(i, "Product_" + vendor + "_", cls=PLATFORM.Product))) for i in c.get_edition(): self.g.add((cpeURI(cveStr), PLATFORM.edition, self.plEnt(i, "Edition_", cls=PLATFORM.Edition))) for i in c.get_language(): self.g.add((cpeURI(cveStr), PLATFORM.language, self.plEnt(i, "Language_", cls=PLATFORM.Language))) for i in c.get_other(): self.g.add((cpeURI(cveStr), PLATFORM.other, self.plEnt(i, "Other_", cls=PLATFORM.Other))) for i in c.get_software_edition(): self.g.add((cpeURI(cveStr), PLATFORM.softwareEdition, self.plEnt(i, "SoftwareEdition_", cls=PLATFORM.SoftwareEdition))) for i in c.get_target_hardware(): self.g.add((cpeURI(cveStr), PLATFORM.targetHardware, self.plEnt(i, "Hardware_", cls=CORE.Hardware))) for i in c.get_target_software(): self.g.add((cpeURI(cveStr), PLATFORM.targetSoftware, self.plEnt(i, "Software_", cls=CORE.Software))) for i in c.get_update(): if not i == "-": self.g.add((cpeURI(cveStr), PLATFORM.update, Literal(i))) for i in c.get_version(): if not i == "-": self.g.add((cpeURI(cveStr), PLATFORM.version, Literal(i))) return subject
def process_advanced_configuration(container: dict) -> dict: ''' Extracts, expands and conditionally expand the CPEs records of a Configuration Container of type ADVANCED. ADVANCED containers usually store information recursively, so we need to iterate over all the underlying containers to extract all CPEs. :param container: A valid container wrapping information about a configuration ''' # First, we need to check the arguments if not container or type(container) is not dict: raise ValueError("container must be a non-empty Parsed JSON Object") if container["configType"] != ConfigType.ADVANCED.name or container[ 'containers'] is None: raise ValueError( "container must be a valid structure of type: {} ".format( ConfigType.ADVANCED.name)) res = defaultdict(lambda: defaultdict(set)) for subcontainer in container['containers']: res.update(process_advanced_configuration(subcontainer)) for complex_cpe in container['cpes']: aux = CPE(complex_cpe['cpe23Uri']) res[aux.get_vendor()[0]][aux.get_product()[0]].update( expand(complex_cpe)) return res
def getinfo(filename): if os.path.exists(filename + '.bin'): print(filename, "exists -- taking in") with open(filename + '.bin', 'rb') as reader: return eval(reader.read()) print(filename, "does not exist, parsing") f = open(filename) tree = etree.parse(f) f.close() vulns = [] entry_nodes = tree.xpath( '//prefix:entry', namespaces={ 'prefix': 'http://scap.nist.gov/schema/feed/vulnerability/2.0' }) for entry in entry_nodes: thisVuln = {} thisVuln['id'] = entry.find(prefixed('vuln', 'cve-id')).text vulnSoftware = entry.find(prefixed('vuln', 'vulnerable-software-list')) if vulnSoftware is not None: for v in vulnSoftware: try: myCPE = CPE(v.text) except NotImplementedError: print("Could not parse") #logging.warning("Unable to parse CPE '%s'" % v.text) else: thisVuln['part'] = myCPE.get_part()[0] thisVuln['vendor'] = myCPE.get_vendor()[0] if 'linux' in thisVuln['vendor']: thisVuln['vendor'] = 'linux' thisVuln['product'] = myCPE.get_product()[0] if 'linux' in thisVuln['product']: thisVuln['vendor'] = 'linux' thisVuln['version'] = myCPE.get_version()[0] thisVuln['update'] = myCPE.get_update()[0] thisVuln['edition'] = myCPE.get_edition()[0] thisVuln['language'] = myCPE.get_language()[0] cvss = entry.find(prefixed('vuln', 'cvss')) if cvss is not None: thisVuln['score'] = cvss.getchildren()[0].getchildren()[0].text thisVuln['accessVector'] = cvss.getchildren()[0].getchildren( )[1].text thisVuln['accessComplexity'] = cvss.getchildren()[0].getchildren( )[2].text thisVuln['auth'] = cvss.getchildren()[0].getchildren()[3].text thisVuln['impactConf'] = cvss.getchildren()[0].getchildren( )[4].text thisVuln['impactInt'] = cvss.getchildren()[0].getchildren()[5].text thisVuln['impactAvail'] = cvss.getchildren()[0].getchildren( )[6].text vulns.append(thisVuln) with open(filename + '.bin', 'wrb+') as myFile: myFile.write(bytes(vulns))
def process_running_on_configuration(container: dict) -> (dict, dict): ''' Extracts, expands and conditionally expand the CPEs records of a Configuration Container of type RUNNING_ON. Generates two lists, one containing the affected CPEs and the other the platforms/execution environment on which they must run. :param container: A valid container wrapping information about a configuration ''' # First, we need to check the arguments if not container or type(container) is not dict: raise ValueError("container must be a non-empty Parsed JSON Object") if container["configType"] != ConfigType.RUNNING_ON.name or container[ 'containers'] is None: raise ValueError( "container must be a valid structure of type: {} ".format( ConfigType.RUNNING_ON.name)) simple_cpes = defaultdict(lambda: defaultdict(set)) running_on = defaultdict(lambda: defaultdict(set)) for subcontainer in container['containers']: if subcontainer['cpeListType'] == "VULNERABLE": for complex_cpe in subcontainer['cpes']: aux = CPE(complex_cpe['cpe23Uri']) simple_cpes[aux.get_vendor()[0]][aux.get_product()[0]].update( expand(complex_cpe)) else: # NON_VULNERABLE for complex_cpe in subcontainer['cpes']: aux = CPE(complex_cpe['cpe23Uri']) running_on[aux.get_vendor()[0]][aux.get_product()[0]].update( expand(complex_cpe)) return simple_cpes, running_on
def extract_cpe(cpe_vector): """Extract vendor and product strings from CPE vector.""" vendor = None product = None try: c = CPE(cpe_vector) vendor = c.get_vendor()[0] product = c.get_product()[0] print("-->", c, vendor, product) except Exception as e: print(e) return vendor, product
def __init__(self, cve_entry, thread: ThreadPool = None): self.id = cve_entry.find(prefixed('vuln', 'cve-id')).text self.vulnsoftware = cve_entry.find( prefixed('vuln', 'vulnerable-software-list')) self.part = [] self.vendor = [] self.version = [] self.update = [] self.edition = [] self.language = [] self.product = [] self.year_path = os.path.join("CVE_Detail", self.get_year()) self.cvssscore = None self.accessVector = None self.accessComplexity = None self.auth = None self.impactConf = None self.impactInt = None self.impactAvail = None if self.vulnsoftware is not None: for product in self.vulnsoftware: try: mycpe = CPE(product.text) except NotImplementedError as e: print(e) else: self.part.append(mycpe.get_part()[0]) self.vendor.append(mycpe.get_vendor()[0]) self.version.append(mycpe.get_version()[0]) self.update.append(mycpe.get_update()[0]) self.edition.append(mycpe.get_edition()[0]) self.language.append(mycpe.get_language()[0]) self.product.append(mycpe.get_product()[0]) cvss = cve_entry.find(prefixed('vuln', 'cvss')) if cvss is not None: self.cvssscore = cvss.getchildren()[0].getchildren()[0].text self.accessVector = cvss.getchildren()[0].getchildren()[1].text self.accessComplexity = cvss.getchildren()[0].getchildren()[2].text self.auth = cvss.getchildren()[0].getchildren()[3].text self.impactConf = cvss.getchildren()[0].getchildren()[4].text self.impactInt = cvss.getchildren()[0].getchildren()[5].text self.impactAvail = cvss.getchildren()[0].getchildren()[6].text self.summery = cve_entry.find(prefixed('vuln', 'summary')).text if thread != None: thread.apply_async(self.get_from_pycvesearch)
def run(mappings): """ Import the Vendors and Products list. """ header("Importing CPE list...") # Download the XML file with timed_operation("Downloading {}...".format(NVD_CPE_URL)): resp = requests.get(NVD_CPE_URL).content # Parse the XML elements with timed_operation("Parsing XML elements..."): raw = gzip.GzipFile(fileobj=BytesIO(resp)).read() obj = untangle.parse(raw.decode("utf-8")) items = obj.cpe_list.cpe_item del obj # Create the objects with timed_operation("Creating list of mappings..."): for item in items: obj = CPE(item.cpe_23_cpe23_item["name"]) vendor = obj.get_vendor()[0] product = obj.get_product()[0] if vendor not in mappings["vendors"].keys(): mappings["vendors"][vendor] = dict(id=get_uuid(), name=vendor) if get_slug(vendor, product) not in mappings["products"].keys(): mappings["products"][get_slug(vendor, product)] = dict( id=get_uuid(), name=product, vendor_id=mappings["vendors"][vendor]["id"], ) del items # Insert the objects in database with timed_operation("Inserting Vendors and Products..."): db.session.bulk_insert_mappings(Vendor, mappings["vendors"].values()) db.session.bulk_insert_mappings(Product, mappings["products"].values()) db.session.commit() info( "{} vendors and {} products imported.".format( len(mappings["vendors"]), len(mappings["products"]) ) ) del mappings
def get_component_name_and_version_from_dependency(self, dependency, related_dependency, namespace): identifiers_node = dependency.find(namespace + 'identifiers') if identifiers_node: # analyzing identifier from the more generic to package_node = identifiers_node.find('.//' + namespace + 'package') if package_node: id = package_node.findtext(f'{namespace}id') purl = PackageURL.from_string(id) purl_parts = purl.to_dict() component_name = purl_parts['namespace'] + ':' if purl_parts[ 'namespace'] and len(purl_parts['namespace']) > 0 else '' component_name += purl_parts['name'] if purl_parts[ 'name'] and len(purl_parts['name']) > 0 else '' component_name = component_name if component_name else None component_version = purl_parts['version'] if purl_parts[ 'version'] and len(purl_parts['version']) > 0 else '' return component_name, component_version # vulnerabilityIds_node = identifiers_node.find('.//' + namespace + 'vulnerabilityIds') # if vulnerabilityIds_node: # id = vulnerabilityIds_node.findtext(f'{namespace}id') # cpe = CPE(id) # component_name = cpe.get_vendor()[0] + ':' if len(cpe.get_vendor()) > 0 else '' # component_name += cpe.get_product()[0] if len(cpe.get_product()) > 0 else '' # component_name = component_name if component_name else None # component_version = cpe.get_version()[0] if len(cpe.get_version()) > 0 else None # return component_name, component_version cpe_node = identifiers_node.find('.//' + namespace + 'identifier[@type="cpe"]') if cpe_node: id = cpe_node.findtext(f'{namespace}name') cpe = CPE(id) component_name = cpe.get_vendor()[0] + ':' if len( cpe.get_vendor()) > 0 else '' component_name += cpe.get_product()[0] if len( cpe.get_product()) > 0 else '' component_name = component_name if component_name else None component_version = cpe.get_version()[0] if len( cpe.get_version()) > 0 else None return component_name, component_version maven_node = identifiers_node.find('.//' + namespace + 'identifier[@type="maven"]') if maven_node: maven_parts = maven_node.findtext(f'{namespace}name').split( ':') # logger.debug('maven_parts:' + str(maven_parts)) if len(maven_parts) == 3: component_name = maven_parts[0] + ':' + maven_parts[1] component_version = maven_parts[2] return component_name, component_version # TODO what happens when there multiple evidencecollectednodes with product or version as type? evidence_collected_node = dependency.find(namespace + 'evidenceCollected') if evidence_collected_node: # <evidenceCollected> # <evidence type="product" confidence="HIGH"> # <source>file</source> # <name>name</name> # <value>jquery</value> # </evidence> # <evidence type="version" confidence="HIGH"> # <source>file</source> # <name>version</name> # <value>3.1.1</value> # </evidence>' # will find the first product and version node. if there are multiple it may not pick the best # since 6.0.0 howoever it seems like there's always a packageurl above so not sure if we need the effort to # implement more logic here product_node = evidence_collected_node.find( './/' + namespace + 'evidence[@type="product"]') if product_node: component_name = product_node.findtext(f'{namespace}value') version_node = evidence_collected_node.find( './/' + namespace + 'evidence[@type="version"]') if version_node: component_version = version_node.findtext( f'{namespace}value') return component_name, component_version return None, None
class Service(object): """ Represents service/application/operating system. Contains basic information: name, version """ _CPE_SPECIAL = r"\!|\"|\;|\#|\$|\%|\&|\'|\(|\)|\+|\,|\/|\:|\<|\=|\>|\@|\[|\]|\^|\`|\{|\||\}|\~|\-" _ESCAPE_CPE = re.compile(_CPE_SPECIAL) _UNESCAPE_CPE = re.compile(r"(\\({0}))".format(_CPE_SPECIAL)) def __init__(self, name=None, version=None, cpe=None): self.name = name self.version = version self._cpe = None self.cpe = cpe @property def cpe(self) -> CPE: """ CPE representation of service """ return self._cpe @cpe.setter def cpe(self, value): if value: self._cpe = CPE(value) @property def cpe_vendor(self) -> str: """ Get vendor name based on CPE """ if isinstance(self._cpe, CPE): return self._unescape_cpe(" ".join(self._cpe.get_vendor())) @property def name_with_version(self) -> str: """ Service name with version included """ if self.version is None or self.name is None: return None return "{name} {version}".format(name=self.name, version=self.version) @property def cpe_product(self) -> str: """ Get product name based on CPE """ if isinstance(self._cpe, CPE): return self._unescape_cpe(" ".join(self._cpe.get_product())) @property def cpe_version(self) -> str: """ Get product name based on CPE """ if isinstance(self._cpe, CPE): return self._unescape_cpe(" ".join(self._cpe.get_version())) def __str__(self): return "{name} {version}".format(name=self.name or '', version=self.version or '').strip() def copy(self) -> 'Service': """ Make copy of service """ return_value = Service(name=self.name, version=self.version) return_value._cpe = self._cpe return return_value @classmethod def _escape_cpe(cls, text: str) -> str: """ Special characters should be escaped before building CPE string """ text = text.lower() def _replace(txt): return r"\{0}".format(txt.group()) if " " in text: raise ValueError("{0}: Space is not allowed in CPE string".format(text)) return cls._ESCAPE_CPE.sub(_replace, text) @classmethod def _unescape_cpe(cls, text: str) -> str: text = text.lower() def _replace(txt): return txt.group()[1] return cls._UNESCAPE_CPE.sub(_replace, text) @classmethod def validate_cpe_arguments(cls, vendor: str, product: str, version: str) -> (str, str, str): """ Validate cpe arguments, and fix as much as possible """ if " " in version: if product.lower() == "ios": version = version.split(" ")[0].strip(",") if vendor == "*": if product.lower() == "ios": vendor = "cisco" return cls._escape_cpe(vendor), cls._escape_cpe(product), cls._escape_cpe(version) @classmethod def build_cpe(cls, part: 'CPEType', vendor: str = '*', product: str = '*', version: str = '*') -> str: """ Build cpe 2.3 string base on vendor, product, version and part """ vendor, product, version = cls.validate_cpe_arguments(vendor=vendor, product=product, version=version) return "cpe:2.3:{part}:{vendor}:{product}:{version}:*:*:*:*:*:*:*".format(part=str(part.value), vendor=vendor, product=product, version=version)
def populate_CVE(root): cve_data = [] vuln_data = [] for entry in root: cve_id = entry.find(prefixed('vuln', 'cve-id')).text cve_id = int(re.sub("[^0-9]", "", cve_id)) pubdate = entry.find(prefixed('vuln', 'published-datetime')).text moddate = entry.find(prefixed('vuln', 'last-modified-datetime')).text summary = entry.find(prefixed('vuln', 'summary')).text pubdate = parser.parse(pubdate) moddate = parser.parse(moddate) vulnSoftware = entry.find(prefixed('vuln', 'vulnerable-software-list')) vulnList = [] unableToParse=0 if vulnSoftware is not None: for v in vulnSoftware: try: myCPE = CPE(v.text) except NotImplementedError: unableToParse+=1 #logging.warning("Unable to parse CPE '%s'" % v.text) else: part = myCPE.get_part()[0] vendor = myCPE.get_vendor()[0] product = myCPE.get_product()[0] version = myCPE.get_version()[0] update = myCPE.get_update()[0] edition = myCPE.get_edition()[0] language = myCPE.get_language()[0] derpa = {"part" : part, "vendor":vendor, "product":product, "version":version, "update":update, "edition":edition, "language":language, "cve":cve_id} vuln_data.append(derpa) if unableToParse>0: logging.warning("Could not parse %d lines from file." % unableToParse) vuln = entry.find(prefixed('vuln','cvss')) #metrics = vuln.find(prefixed('cvss','base_metrics')) if vuln is not None: score = vuln.getchildren()[0].getchildren()[0].text accessVector = vuln.getchildren()[0].getchildren()[1].text accessComplexity = vuln.getchildren()[0].getchildren()[2].text auth = vuln.getchildren()[0].getchildren()[3].text impactConf = vuln.getchildren()[0].getchildren()[4].text impactInt = vuln.getchildren()[0].getchildren()[5].text impactAvail = vuln.getchildren()[0].getchildren()[6].text if "DO NOT USE THIS CANDIDATE NUMBER" not in summary: data = { "cve":cve_id, "pubdate":pubdate, "moddate":moddate, "summary":summary, "score":score, "accessVector":accessVector, "accessComp":accessComplexity, "auth":auth, "impactConf": impactConf, "impactInt": impactInt, "impactAvail": impactAvail } cve_data.append(data) tables['CVEs'].insert().execute(cve_data) tables['VulnSoftware'].insert().execute(vuln_data)
def extract_vendor_product_version(cpe_str): """Extract vendor and product from NVD cve entry.""" cpe_ = CPE(cpe_str) return cpe_.get_vendor()[0], cpe_.get_product()[0], cpe_.get_version()[0]
def get_component_name_and_version_from_dependency(self, dependency, related_dependency, namespace): component_name, component_version = None, None # big try catch to avoid crashint the parser on some unexpected stuff try: identifiers_node = dependency.find(namespace + 'identifiers') if identifiers_node: # <identifiers> # <identifier type="cpe" confidence="HIGHEST"> # <name>cpe:/a:apache:xalan-java:2.7.1</name> # <url>https://web.nvd.nist.gov/view/vuln/search-results?adv_search=true&cves=on&cpe_version=cpe%3A%2Fa%3Aapache%3Axalan-java%3A2.7.1</url> # </identifier> # <identifier type="maven" confidence="HIGHEST"> # <name>xalan:serializer:2.7.1</name> # <url>https://search.maven.org/remotecontent?filepath=xalan/serializer/2.7.1/serializer-2.7.1.jar</url> # </identifier> # </identifiers> # newly found in v6.0.0 # <identifiers> # <package confidence="HIGH"> # <id>pkg:maven/nl.isaac.client.offerservice/[email protected]</id> # <url>https://ossindex.sonatype.org/component/pkg:maven/nl.isaac.client.offerservice/[email protected]</url> # </package> # </identifiers> # <identifiers> # <package confidence="HIGHEST"> # <id>pkg:npm/[email protected]</id> # <url>https://ossindex.sonatype.org/component/pkg:npm/[email protected]</url> # </package> # </identifiers> package_node = identifiers_node.find('.//' + namespace + 'package') if package_node: logger.debug( 'package string: ' + self.get_field_value(package_node, 'id', namespace)) id = self.get_field_value(package_node, 'id', namespace) purl = PackageURL.from_string(id) purl_parts = purl.to_dict() component_name = purl_parts[ 'namespace'] + ':' if purl_parts['namespace'] and len( purl_parts['namespace']) > 0 else '' component_name += purl_parts['name'] if purl_parts[ 'name'] and len(purl_parts['name']) > 0 else '' component_name = component_name if component_name else None component_version = purl_parts['version'] if purl_parts[ 'version'] and len(purl_parts['version']) > 0 else '' return component_name, component_version cpe_node = identifiers_node.find('.//' + namespace + 'identifier[@type="cpe"]') if cpe_node: # logger.debug('cpe string: ' + self.get_field_value(cpe_node, 'name')) cpe = CPE(self.get_field_value(cpe_node, 'name')) component_name = cpe.get_vendor()[0] + ':' if len( cpe.get_vendor()) > 0 else '' component_name += cpe.get_product()[0] if len( cpe.get_product()) > 0 else '' component_name = component_name if component_name else None component_version = cpe.get_version()[0] if len( cpe.get_version()) > 0 else None # logger.debug('get_edition: ' + str(cpe.get_edition())) # logger.debug('get_language: ' + str(cpe.get_language())) # logger.debug('get_part: ' + str(cpe.get_part())) # logger.debug('get_software_edition: ' + str(cpe.get_software_edition())) # logger.debug('get_target_hardware: ' + str(cpe.get_target_hardware())) # logger.debug('get_target_software: ' + str(cpe.get_target_software())) # logger.debug('get_vendor: ' + str(cpe.get_vendor())) # logger.debug('get_update: ' + str(cpe.get_update())) return component_name, component_version maven_node = identifiers_node.find('.//' + namespace + 'identifier[@type="maven"]') if maven_node: # logger.debug('maven_string: ' + self.get_field_value(maven_node, 'name')) maven_parts = self.get_field_value(maven_node, 'name', namespace).split(':') # logger.debug('maven_parts:' + str(maven_parts)) if len(maven_parts) == 3: component_name = maven_parts[0] + ':' + maven_parts[1] component_version = maven_parts[2] return component_name, component_version # TODO # include identifiers in description? # <identifiers> # <package confidence="HIGH"> # <id>pkg:maven/org.dom4j/[email protected]</id> # <url>https://ossindex.sonatype.org/component/pkg:maven/org.dom4j/[email protected]</url> # </package> # <vulnerabilityIds confidence="HIGHEST"> # <id>cpe:2.3:a:dom4j_project:dom4j:2.1.1.hat-00001:*:*:*:*:*:*:*</id> # <url>https://nvd.nist.gov/vuln/search/results?form_type=Advanced&results_type=overview&search_type=all&cpe_vendor=cpe%3A%2F%3Adom4j_project&cpe_product=cpe%3A%2F%3Adom4j_project%3Adom4j&cpe_version=cpe%3A%2F%3Adom4j_project%3Adom4j%3A2.1.1.hat-00001</url> # </vulnerabilityIds> # TODO what happens when there multiple evidencecollectednodes with product or version as type? evidence_collected_node = dependency.find(namespace + 'evidenceCollected') if evidence_collected_node: # <evidenceCollected> # <evidence type="product" confidence="HIGH"> # <source>file</source> # <name>name</name> # <value>jquery</value> # </evidence> # <evidence type="version" confidence="HIGH"> # <source>file</source> # <name>version</name> # <value>3.1.1</value> # </evidence>' # will find the first product and version node. if there are multiple it may not pick the best # since 6.0.0 howoever it seems like there's always a packageurl above so not sure if we need the effort to # implement more logic here product_node = evidence_collected_node.find( './/' + namespace + 'evidence[@type="product"]') if product_node: component_name = self.get_field_value( product_node, 'value', namespace) version_node = evidence_collected_node.find( './/' + namespace + 'evidence[@type="version"]') if version_node: component_version = self.get_field_value( version_node, 'value', namespace) return component_name, component_version except: logger.exception( 'error parsing component_name and component_version') logger.debug( 'dependency: %s', ElementTree.tostring(dependency, encoding='utf8', method='xml')) return component_name, component_version
def get_cpe_df(self, debug=False): """Get the list of CPE names for the vulnerability. """ type_list = [] part_list = [] vendor_list = [] product_list = [] version_list = [] update_list = [] edition_list = [] language_list = [] sw_edition_list = [] target_sw_list = [] target_hw_list = [] other_list = [] published_datetime_list = [] for cpe_entry in self.cpe_list: #if(debug): #print(cpe_entry) try: cp = CPE(cpe_entry) if(cp.is_hardware()): type_list.append("HW") elif(cp.is_operating_system()): type_list.append("OS") elif(cp.is_application()): type_list.append("APP") else: type_list.append("UNDEFINED") part_list.append(list_to_string(cp.get_part())) vendor_list.append(list_to_string(cp.get_vendor())) product_list.append(list_to_string(cp.get_product())) version_list.append(list_to_string(cp.get_version())) update_list.append(list_to_string(cp.get_update())) edition_list.append(list_to_string(cp.get_edition())) language_list.append(list_to_string(cp.get_language())) sw_edition_list.append(list_to_string(cp.get_software_edition())) target_sw_list.append(list_to_string(cp.get_target_software())) target_hw_list.append(list_to_string(cp.get_target_hardware())) other_list.append(list_to_string(cp.get_other())) published_datetime_list.append(self.published_datetime) except Exception as inst: print(inst) data = pd.DataFrame() data['type'] = type_list data['part'] = part_list data['vendor'] = vendor_list data['product'] = product_list data['version'] = version_list data['update'] = update_list data['edition'] = edition_list data['language'] = language_list data['sw_edition'] = sw_edition_list data['target_sw'] = target_sw_list data['target_hw'] = target_hw_list data['other'] = other_list data['published_datetime'] = published_datetime_list return data
def get_item(vuln, test): finding = Finding( test=test, unique_id_from_tool=vuln["id"], nb_occurences=1, ) # Defining variables location = vuln["location"] # Endpoint # using url if "url" in location and location["url"] and location["url"] != "None": endpoint = Endpoint.from_uri(location["url"]) # fallback to using old way of creating endpoints elif "domain" in location and location["domain"] and location["domain"] != "None": endpoint = Endpoint(host=str(location["domain"])) else: # no domain, use ip instead if "ip" in location and location["ip"] and location["ip"] != "None": endpoint = Endpoint(host=str(location["ip"])) # check for protocol if ( "applicationProtocol" in location and location["applicationProtocol"] and location["applicationProtocol"] != "None" ): endpoint.protocol = location["applicationProtocol"] # check for port if ( "port" in location and location["port"] in location and location["port"] != "None" ): endpoint.port = location["port"] finding.unsaved_endpoints = [endpoint] # assigning endpoint # Title finding.title = vuln["name"] # Description + CVEs description = vuln["classification"] cves = "no match" if "CVE-NO-MATCH" not in vuln["kb"]["cves"]: finding.cve = vuln["kb"]["cves"][0] cves = "" for cve in vuln["kb"]["cves"]: cves += f"{cve}, " cves = cves[: len(cves) - 2] # removing the comma and the blank space finding.description = description + "; CVEs: " + cves finding.severity = vuln["severity"].title() # Date date_str = vuln["createdOn"] date_str = date_str[: len(date_str) - 3] + date_str[-2:] finding.date = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S.%f%z") # Component Name and Version if ( "applicationCpe" in location and location["applicationCpe"] and location["applicationCpe"] != "None" ): cpe = CPE(location["applicationCpe"]) component_name = cpe.get_vendor()[0] + ":" if len( cpe.get_vendor()) > 0 else "" component_name += cpe.get_product()[0] if len( cpe.get_product()) > 0 else "" finding.component_name = component_name if component_name else None finding.component_version = ( cpe.get_version()[0] if len(cpe.get_version()) > 0 else None ) return finding
def populate_CVE(root): cve_data = [] vuln_data = [] for entry in root: cve_id = entry.find(prefixed("vuln", "cve-id")).text cve_id = int(re.sub("[^0-9]", "", cve_id)) pubdate = entry.find(prefixed("vuln", "published-datetime")).text moddate = entry.find(prefixed("vuln", "last-modified-datetime")).text summary = entry.find(prefixed("vuln", "summary")).text pubdate = parser.parse(pubdate) moddate = parser.parse(moddate) vulnSoftware = entry.find(prefixed("vuln", "vulnerable-software-list")) vulnList = [] unableToParse = 0 if vulnSoftware is not None: for v in vulnSoftware: try: myCPE = CPE(v.text) except NotImplementedError: unableToParse += 1 # logging.warning("Unable to parse CPE '%s'" % v.text) else: part = myCPE.get_part()[0] vendor = myCPE.get_vendor()[0] product = myCPE.get_product()[0] version = myCPE.get_version()[0] update = myCPE.get_update()[0] edition = myCPE.get_edition()[0] language = myCPE.get_language()[0] derpa = { "part": part, "vendor": vendor, "product": product, "version": version, "update": update, "edition": edition, "language": language, "cve": cve_id, } vuln_data.append(derpa) if unableToParse > 0: logging.warning("Could not parse %d lines from file." % unableToParse) vuln = entry.find(prefixed("vuln", "cvss")) # metrics = vuln.find(prefixed('cvss','base_metrics')) if vuln is not None: score = vuln.getchildren()[0].getchildren()[0].text accessVector = vuln.getchildren()[0].getchildren()[1].text accessComplexity = vuln.getchildren()[0].getchildren()[2].text auth = vuln.getchildren()[0].getchildren()[3].text impactConf = vuln.getchildren()[0].getchildren()[4].text impactInt = vuln.getchildren()[0].getchildren()[5].text impactAvail = vuln.getchildren()[0].getchildren()[6].text if "DO NOT USE THIS CANDIDATE NUMBER" not in summary: data = { "cve": cve_id, "pubdate": pubdate, "moddate": moddate, "summary": summary, "score": score, "accessVector": accessVector, "accessComp": accessComplexity, "auth": auth, "impactConf": impactConf, "impactInt": impactInt, "impactAvail": impactAvail, } cve_data.append(data) tables["CVEs"].insert().execute(cve_data) tables["VulnSoftware"].insert().execute(vuln_data)