From 5ab2ac41a6abc19427f1824bab7bc1096088ebe1 Mon Sep 17 00:00:00 2001 From: David Redmin Date: Thu, 5 May 2022 16:25:34 -0400 Subject: [PATCH 1/8] Add cvss_version field to CVE collection --- cyhy/db/database.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cyhy/db/database.py b/cyhy/db/database.py index b82e0e7..d71ff1e 100644 --- a/cyhy/db/database.py +++ b/cyhy/db/database.py @@ -1568,8 +1568,13 @@ def children(self): class CVEDoc(RootDoc): __collection__ = CVE_COLLECTION - structure = {"_id": basestring, "cvss_score": float, "severity": int} # CVE String - required_fields = ["_id", "cvss_score", "severity"] + structure = { + "_id": basestring, # CVE string + "cvss_score": float, + "cvss_version": basestring, + "severity": int + } + required_fields = ["_id", "cvss_score", "cvss_version", "severity"] default_values = {} def get_indices(self): From 553f39984d4835652ac9b28bc3c22bbcc1f71d05 Mon Sep 17 00:00:00 2001 From: David Redmin Date: Thu, 5 May 2022 16:28:17 -0400 Subject: [PATCH 2/8] Update severity calculation to include CVSSv3 Previously, CVSSv2 severities were assumed. --- cyhy/db/database.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/cyhy/db/database.py b/cyhy/db/database.py index d71ff1e..0c7bc68 100644 --- a/cyhy/db/database.py +++ b/cyhy/db/database.py @@ -1582,15 +1582,26 @@ def get_indices(self): def save(self, *args, **kwargs): # Calculate severity from cvss on save + # Source: https://nvd.nist.gov/vuln-metrics/cvss cvss = self["cvss_score"] - if cvss == 10: - self["severity"] = 4 - elif cvss >= 7.0: - self["severity"] = 3 - elif cvss >= 4.0: - self["severity"] = 2 - else: - self["severity"] = 1 + if self["cvss_version"] == "2.0": + if cvss == 10: + self["severity"] = 4 + elif cvss >= 7.0: + self["severity"] = 3 + elif cvss >= 4.0: + self["severity"] = 2 + else: + self["severity"] = 1 + elif self["cvss_version"] in ["3.0", "3.1"]: + if cvss >= 9.0: + self["severity"] = 4 + elif cvss >= 7.0: + self["severity"] = 3 + elif cvss >= 4.0: + self["severity"] = 2 + else: + self["severity"] = 1 super(CVEDoc, self).save(*args, **kwargs) From 1ad23ec828c956bcce38a39da52d18ae04bf3b3d Mon Sep 17 00:00:00 2001 From: David Redmin Date: Thu, 5 May 2022 16:37:19 -0400 Subject: [PATCH 3/8] Support CVSSv3 scores and store CVSS version If a v3 score exists, store it in our database. If not, store the v2 score. --- bin/cyhy-nvdsync | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bin/cyhy-nvdsync b/bin/cyhy-nvdsync index 82bffc4..f4e9478 100755 --- a/bin/cyhy-nvdsync +++ b/bin/cyhy-nvdsync @@ -43,15 +43,24 @@ def parse_json(db, json_stream): for entry in data.get("CVE_Items", []): cve_id = entry["cve"]["CVE_data_meta"]["ID"] - if "baseMetricV2" not in entry["impact"]: - # NVD 'reject' CVEs do not have 'baseMetricV2' CVSS data + if ("baseMetricV2" or "baseMetricV3") not in entry["impact"]: + # Reject CVEs that don't have baseMetricV2 or baseMetricV3 CVSS data # Make sure they are removed from our db. db.CVEDoc.collection.remove({"_id": cve_id}, safe=False) print "x", else: print ".", - cvss_base_score = entry["impact"]["baseMetricV2"]["cvssV2"]["baseScore"] - entry_doc = db.CVEDoc({"_id": cve_id, "cvss_score": float(cvss_base_score)}) + if "baseMetricV3" in entry["impact"]: + cvss_base_score = entry["impact"]["baseMetricV3"]["cvssV3"]["baseScore"] + cvss_version = entry["impact"]["baseMetricV3"]["cvssV3"]["version"] + else: + cvss_base_score = entry["impact"]["baseMetricV2"]["cvssV2"]["baseScore"] + cvss_version = entry["impact"]["baseMetricV2"]["cvssV2"]["version"] + entry_doc = db.CVEDoc({ + "_id": cve_id, + "cvss_score": float(cvss_base_score), + "cvss_version": cvss_version + }) entry_doc.save(safe=False) print "\n\n" From d5e722e2a9ca630064e02d7d6a5c8f354c98538d Mon Sep 17 00:00:00 2001 From: David Redmin Date: Fri, 6 May 2022 14:12:30 -0400 Subject: [PATCH 4/8] Use cleaner conditional logic Co-authored-by: Shane Frasier --- bin/cyhy-nvdsync | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/bin/cyhy-nvdsync b/bin/cyhy-nvdsync index f4e9478..307fa75 100755 --- a/bin/cyhy-nvdsync +++ b/bin/cyhy-nvdsync @@ -50,12 +50,9 @@ def parse_json(db, json_stream): print "x", else: print ".", - if "baseMetricV3" in entry["impact"]: - cvss_base_score = entry["impact"]["baseMetricV3"]["cvssV3"]["baseScore"] - cvss_version = entry["impact"]["baseMetricV3"]["cvssV3"]["version"] - else: - cvss_base_score = entry["impact"]["baseMetricV2"]["cvssV2"]["baseScore"] - cvss_version = entry["impact"]["baseMetricV2"]["cvssV2"]["version"] + version = "V3" if "baseMetricV3" in entry["impact"] else "V2" + cvss_base_score = entry["impact"]["baseMetric" + version]["cvss" + version]["baseScore"] + cvss_version = entry["impact"]["baseMetric" + version]["cvss" + version]["version"] entry_doc = db.CVEDoc({ "_id": cve_id, "cvss_score": float(cvss_base_score), From 2b81487cd5dd6c1a8e54538254629bc30fb1c980 Mon Sep 17 00:00:00 2001 From: David Redmin Date: Tue, 10 May 2022 16:17:08 -0400 Subject: [PATCH 5/8] Prefer CVSS v3 score over v2 and add cvss_version to ticket details --- cyhy/db/ticket_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cyhy/db/ticket_manager.py b/cyhy/db/ticket_manager.py index 8cef0b0..00d25bd 100644 --- a/cyhy/db/ticket_manager.py +++ b/cyhy/db/ticket_manager.py @@ -96,7 +96,8 @@ def __generate_ticket_details(self, vuln, ticket, check_for_changes=True): new_details = { "cve": vuln.get("cve"), - "cvss_base_score": vuln["cvss_base_score"], + "cvss_base_score": vuln.get("cvss3_base_score", vuln["cvss_base_score"]), + "cvss_version": "3" if "cvss3_base_score" in vuln else "2", "kev": False, "name": vuln["plugin_name"], "score_source": vuln["source"], @@ -108,6 +109,7 @@ def __generate_ticket_details(self, vuln, ticket, check_for_changes=True): cve_doc = self.__db.CVEDoc.find_one({"_id": vuln["cve"]}) if cve_doc: new_details["cvss_base_score"] = cve_doc["cvss_score"] + new_details["cvss_version"] = cve_doc["cvss_version"] new_details["score_source"] = "nvd" new_details["severity"] = cve_doc["severity"] # if the CVE is listed in the KEV collection, we'll mark it as such From 79d29f5c7e2a515aa8c465265e6f6889334fd987 Mon Sep 17 00:00:00 2001 From: David Redmin Date: Wed, 11 May 2022 11:37:05 -0400 Subject: [PATCH 6/8] Ensure ticket severity correctly matches CVSS score As of May 2022, some Nessus plugins report a severity that is inconsistent with their (non-NVD, non-CVE-based) CVSS v3 score. To reduce confusion, we ensure that the severity is correct here. For examples, see the following plugins: 34460, 104572, 107056, 140770, 156560, 156941, 156441 --- cyhy/db/ticket_manager.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cyhy/db/ticket_manager.py b/cyhy/db/ticket_manager.py index 00d25bd..0e92737 100644 --- a/cyhy/db/ticket_manager.py +++ b/cyhy/db/ticket_manager.py @@ -117,6 +117,33 @@ def __generate_ticket_details(self, vuln, ticket, check_for_changes=True): if kev_doc: new_details["kev"] = True + # As of May 2022, some Nessus plugins report a severity that is + # inconsistent with their (non-NVD, non-CVE-based) CVSS v3 score. + # To reduce confusion, we ensure that the severity is correct here. + # For examples, see the following plugins: + # 34460, 104572, 107056, 140770, 156560, 156941, 156441 + if new_details["score_source"] != "nvd": + cvss = new_details["cvss_base_score"] + # Source: https://nvd.nist.gov/vuln-metrics/cvss + if new_details["cvss_version"] == "2": + if cvss == 10: + new_details["severity"] = 4 + elif cvss >= 7.0: + new_details["severity"] = 3 + elif cvss >= 4.0: + new_details["severity"] = 2 + else: + new_details["severity"] = 1 + elif new_details["cvss_version"] == "3": + if cvss >= 9.0: + new_details["severity"] = 4 + elif cvss >= 7.0: + new_details["severity"] = 3 + elif cvss >= 4.0: + new_details["severity"] = 2 + else: + new_details["severity"] = 1 + delta = [] if check_for_changes: delta = self.__calculate_delta(ticket["details"], new_details) From ea1a9dc14ea590f7c4c55dbd87f8180a790d321e Mon Sep 17 00:00:00 2001 From: David Redmin Date: Mon, 16 May 2022 13:19:13 -0400 Subject: [PATCH 7/8] Add comments about CVSS score to severity mapping Co-authored-by: Mark Feldhousen --- cyhy/db/database.py | 13 +++++++++++++ cyhy/db/ticket_manager.py | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/cyhy/db/database.py b/cyhy/db/database.py index 0c7bc68..12c08c2 100644 --- a/cyhy/db/database.py +++ b/cyhy/db/database.py @@ -1583,6 +1583,19 @@ def get_indices(self): def save(self, *args, **kwargs): # Calculate severity from cvss on save # Source: https://nvd.nist.gov/vuln-metrics/cvss + # + # Notes: + # - The CVSS score to severity mapping is not continuous (e.g. a + # score of 8.95 is undefined according to their table). However, + # the CVSS equation documentation + # (https://www.first.org/cvss/specification-document#CVSS-v3-1-Equations) + # specifies that all CVSS scores are rounded up to the nearest tenth + # of a point, so our severity mapping below is valid. + # - CVSSv3 specifies that a score of 0.0 has a severity of "None", but + # we have chosen to map 0.0 to severity 1 ("Low") because CyHy code + # has historically assumed severities between 1 and 4 (inclusive). + # Since we have not seen CVSSv3 scores lower than 3.1, this will + # hopefully never be an issue. cvss = self["cvss_score"] if self["cvss_version"] == "2.0": if cvss == 10: diff --git a/cyhy/db/ticket_manager.py b/cyhy/db/ticket_manager.py index 0e92737..893df76 100644 --- a/cyhy/db/ticket_manager.py +++ b/cyhy/db/ticket_manager.py @@ -125,6 +125,19 @@ def __generate_ticket_details(self, vuln, ticket, check_for_changes=True): if new_details["score_source"] != "nvd": cvss = new_details["cvss_base_score"] # Source: https://nvd.nist.gov/vuln-metrics/cvss + # + # Notes: + # - The CVSS score to severity mapping is not continuous (e.g. a + # score of 8.95 is undefined according to their table). + # However, the CVSS equation documentation + # (https://www.first.org/cvss/specification-document#CVSS-v3-1-Equations) + # specifies that all CVSS scores are rounded up to the nearest + # tenth of a point, so our severity mapping below is valid. + # - CVSSv3 specifies that a score of 0.0 has a severity of "None", + # but we have chosen to map 0.0 to severity 1 ("Low") because + # CyHy code has historically assumed severities between 1 and 4 + # (inclusive). Since we have not seen CVSSv3 scores lower than + # 3.1, this will hopefully never be an issue. if new_details["cvss_version"] == "2": if cvss == 10: new_details["severity"] = 4 From 5212891a3525147824178fb193385a79eab0375d Mon Sep 17 00:00:00 2001 From: dav3r Date: Wed, 8 Jun 2022 13:06:23 -0400 Subject: [PATCH 8/8] Move comment to a better location Co-authored-by: Nick <50747025+mcdonnnj@users.noreply.github.com> --- bin/cyhy-nvdsync | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cyhy-nvdsync b/bin/cyhy-nvdsync index 307fa75..f6885ff 100755 --- a/bin/cyhy-nvdsync +++ b/bin/cyhy-nvdsync @@ -43,8 +43,8 @@ def parse_json(db, json_stream): for entry in data.get("CVE_Items", []): cve_id = entry["cve"]["CVE_data_meta"]["ID"] + # Reject CVEs that don't have baseMetricV2 or baseMetricV3 CVSS data if ("baseMetricV2" or "baseMetricV3") not in entry["impact"]: - # Reject CVEs that don't have baseMetricV2 or baseMetricV3 CVSS data # Make sure they are removed from our db. db.CVEDoc.collection.remove({"_id": cve_id}, safe=False) print "x",