Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support CVSSv3 scores and severities #69

Merged
merged 8 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions bin/cyhy-nvdsync
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,21 @@ 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
# Reject CVEs that don't have baseMetricV2 or baseMetricV3 CVSS data
if ("baseMetricV2" or "baseMetricV3") not in entry["impact"]:
# 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)})
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),
"cvss_version": cvss_version
})
entry_doc.save(safe=False)
print "\n\n"

Expand Down
49 changes: 39 additions & 10 deletions cyhy/db/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -1568,24 +1568,53 @@ 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):
return tuple()

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 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)


Expand Down
44 changes: 43 additions & 1 deletion cyhy/db/ticket_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand All @@ -108,13 +109,54 @@ 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
kev_doc = self.__db.KEVDoc.find_one({"_id": vuln["cve"]})
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
#
# 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
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)
Expand Down