Skip to content
This repository has been archived by the owner on Jun 8, 2023. It is now read-only.

Commit

Permalink
Using a library to calculate CVSS
Browse files Browse the repository at this point in the history
  • Loading branch information
masonhfisher committed Oct 6, 2021
1 parent 1126952 commit 501625f
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 502 deletions.
File renamed without changes.
108 changes: 23 additions & 85 deletions cve_lookup/vector/cvss2.py
Original file line number Diff line number Diff line change
@@ -1,97 +1,35 @@
# Equations from:
# https://nvd.nist.gov/vuln-metrics/cvss/v2-calculator/equations
# (As if 7/17/2021)

from . import exceptions
from cvss import CVSS2

class cvss2_vector:
_version = 2.0

def score_case(self, vector, values):
# Basically a switch case statement, Python 3.10 implements something simular but this exists for compatibility purposes with older Python versions
for case in values:
if self.vector[vector] == case:
return values[case]
raise exceptions.InvalidVectorValue(vector, self.vector)

def calculate_base(self):
# Base score calculation
access_vector = self.score_case('AV', {'L':0.395, 'A':0.646, 'N':1})
access_complexity = self.score_case('AC', {'H':0.35, 'M':0.61, 'L':0.71})
authentication = self.score_case('Au', {'M':0.45, 'S':0.56, 'N':0.704})
confidentality_impact = self.score_case('C', {'N':0, 'P':0.275, 'C':0.660})
integrity_impact = self.score_case('I', {'N':0, 'P':0.275, 'C':0.660})
availability_impact = self.score_case('A', {'N':0, 'P':0.275, 'C':0.660})

self.score_impact = 10.41 * (1 - (1 - confidentality_impact) * (1 - integrity_impact) * (1 - availability_impact))
self.score_exploitability = 20 * access_complexity * authentication * access_vector
fimpact = 0
if self.score_impact != 0:
fimpact = 1.176
return (.6*self.score_impact+.4*self.score_exploitability-1.5)*fimpact

def calculate_temporal(self, score_base):
exploitability = self.score_case('E', {'ND':1, 'U':0.85, 'POC':0.9, 'F':0.95, 'H':1})
remediation_level = self.score_case('RL', {'ND':1, 'OF':0.87, 'TF':0.9, 'W':0.95, 'U':1})
report_confidence = self.score_case('RC', {'ND':1, 'UC':0.9, 'UR':0.95, 'C':1})
return score_base * exploitability * remediation_level * report_confidence

def calculate_environment(self, score_base):
collateral_damage_potential = self.score_case('CDP', {'ND':0, 'N':0, 'L':0.1, 'LM':0.3, 'MH':0.4, 'H':0.5})
target_distribution = self.score_case('TD', {'ND':1, 'N':0, 'L':0.25, 'M':0.75, 'H':1})

confidentality_requirement = self.score_case('CR', {'ND':1, 'L':0.5, 'M':1, 'H':1.51})
integrity_requirement = self.score_case('IR', {'ND':1, 'L':0.5, 'M':1, 'H':1.51})
availability_requirement = self.score_case('AR', {'ND':1, 'L':0.5, 'M':1, 'H':1.51})

# The three variables below this comment is from the Base Score Metrics but are needed for this equation
confidentality_impact = self.score_case('C', {'N':0, 'P':0.275, 'C':0.660})
integrity_impact = self.score_case('I', {'N':0, 'P':0.275, 'C':0.660})
availability_impact = self.score_case('A', {'N':0, 'P':0.275, 'C':0.660})

self.modified_impact = min(10, 10.41 * (1 -
(1 - confidentality_impact * confidentality_requirement)
* (1 - integrity_impact * integrity_requirement)
* (1- availability_impact * availability_requirement)))

# "modified_temporal" is the Temporal Score recomputed with the impact sub-equation replaced with the "modified_impact" variable
fimpact = 0
if self.score_impact != 0:
fimpact = 1.176
modified_temporal = self.calculate_temporal((.6*self.modified_impact+.4*self.score_exploitability-1.5)*fimpact)
return (modified_temporal + (10 - modified_temporal) * collateral_damage_potential) * target_distribution

def calculate_overall(self, environment_vectors, temporal_vectors, not_defined='X'):
# Calculate all scores (if needed) and the overall score
self.score_base = self.calculate_base()
self.score_temporal = self.calculate_temporal(self.score_base)
self.score_environmental = self.calculate_environment(self.score_base)

for vector in environment_vectors:
if self.vector[vector] != not_defined:
return self.score_environmental

for vector in temporal_vectors:
if self.vector[vector] != not_defined:
return self.score_temporal

return self.score_base

def __init__(self, vector):
# Initialization

# Initialize vector here
self.vector = {'AV':None, 'AC':None, 'Au':None, 'C':None, 'I':None, 'A':None, 'E':'ND', 'RL':'ND', 'RC':'ND', 'CDP':'ND', 'TD':'ND', 'CR':'ND', 'IR':'ND', 'AR':'ND'}

# Format vector
if vector[0] == '(':
self.vector_txt = vector[1:-1]
self.vector = vector[1:-1]
else:
self.vector_txt = vector
vector_list = self.vector_txt.split('/')
for vectors in vector_list:
vectorsp = vectors.split(':')
self.vector[vectorsp[0]] = vectorsp[1]
self.score_overall = max(0, min(10, self.calculate_overall(['CDP', 'TD', 'CR', 'IR', 'AR'], ['E', 'RL', 'RC'], 'ND')))
self.vector = vector

# Prep vector
self._vector = CVSS2(self.vector)
self.vector_clean = self._vector.clean_vector()

# Scores
self.base_score = self._vector.scores()[0]
self.temporal_score = self._vector.scores()[1]
self.environmental_score = self._vector.scores()[2]

# Overall score
if self.environmental_score != None:
self.score_overall = self.environmental_score
elif self.temporal_score != None:
self.score_overall = self.temporal_score
else:
self.score_overall = self.base_score

# Score name
if self.score_overall >= 7:
self.score_name = "High"
elif self.score_overall >= 4:
Expand Down
113 changes: 17 additions & 96 deletions cve_lookup/vector/cvss3.py
Original file line number Diff line number Diff line change
@@ -1,109 +1,30 @@
# Equations from:
# https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator/v30/equations
# Metric levels from:
# https://www.first.org/cvss/v3.0/specification-document#8-4-Metrics-Levels
# (As if 7/26/2021)

from math import ceil
from . import exceptions
from cvss import CVSS3
from . import cvss2

class cvss3_vector(cvss2.cvss2_vector):
_version = 3.0

round_up = lambda self, num : ceil(num*10)/10

def calculate_base(self):
# Base score calculation
scope = self.score_case('S', {'U': False, 'C': True}) # True if scope changed
attack_vector = self.score_case('AV', {'N':0.85, 'A':0.62, 'L':0.55, 'P':0.2})
attack_complex = self.score_case('AC', {'L':0.77, 'H':0.44})
if scope:
privileges_req = self.score_case('PR', {'N':0.85, 'L':0.68, 'H':0.50})
else:
privileges_req = self.score_case('PR', {'N':0.85, 'L':0.62, 'H':0.27})
user_interaction = self.score_case('UI', {'N':0.85, 'R':0.62})

confidentality_impact = self.score_case('C', {'N':0, 'L':0.22, 'H':0.56})
integrity_impact = self.score_case('I', {'N':0, 'L':0.22, 'H':0.56})
availability_impact = self.score_case('A', {'N':0, 'L':0.22, 'H':0.56})

self.score_impact_base = 1 - ((1 - confidentality_impact) * (1 - integrity_impact) * (1 - availability_impact))
if scope:
self.score_impact = 7.52 * (self.score_impact_base - 0.029) - 3.25 * (self.score_impact_base - 0.02)**15
else:
self.score_impact = 6.42 * self.score_impact_base
self.score_exploitability = 8.22 * attack_vector * attack_complex * privileges_req * user_interaction

if self.score_impact <= 0:
return 0
elif scope:
return self.round_up(min(1.08 * (self.score_impact + self.score_exploitability), 10))
else:
return self.round_up(min(self.score_impact + self.score_exploitability, 10))

def calculate_temporal(self, score_base):
exploitability = self.score_case('E', {'X':1, 'U':0.91, 'P':0.94, 'F':0.97, 'H':1})
remediation_level = self.score_case('RL', {'X':1, 'O':0.95, 'T':0.96, 'W':0.97, 'U':1})
report_confidence = self.score_case('RC', {'X':1, 'U':0.92, 'R':0.96, 'C':1})
return self.round_up(score_base * exploitability * remediation_level * report_confidence)

def calculate_environment(self, score_base):
scope = self.score_case('MS', {'X':False, 'U':False, 'C':True})
attack_vector = self.score_case('MAV', {'X':0.2, 'N':0.85, 'A':0.62, 'L':0.55, 'P':0.2})
attack_complex = self.score_case('MAC', {'X':0.44, 'L':0.77, 'H':0.44})
privileges_req = self.score_case('MPR', {'X':0.68, 'N':0.85, 'L':0.68, 'H':0.50})
user_interaction = self.score_case('MUI', {'X':0.62, 'N':0.85, 'R':0.62})
confidentality_impact = self.score_case('MC', {'X': 0.56, 'N':0, 'L':0.22, 'H':0.56})
integrity_impact = self.score_case('MI', {'X': 0.56, 'N':0, 'L':0.22, 'H':0.56})
availability_impact = self.score_case('MA', {'X':0.56, 'N':0, 'L':0.22, 'H':0.56})
confidentality_requirement = self.score_case('CR', {'X':1, 'L':0.5, 'M':1, 'H':1.5})
integrity_requirement = self.score_case('IR', {'X':1, 'L':0.5, 'M':1, 'H':1.5})
availability_requirement = self.score_case('AR', {'X':1, 'L':0.5, 'M':1, 'H':1.5})

exploitability = self.score_case('E', {'X':1, 'U':0.91, 'P':0.94, 'F':0.97, 'H':1})
remediation_level = self.score_case('RL', {'X':1, 'O':0.95, 'T':0.96, 'W':0.97, 'U':1})
report_confidence = self.score_case('RC', {'X':1, 'U':0.92, 'R':0.96, 'C':1})

exploitability = 8.22 * attack_vector * attack_complex * privileges_req * user_interaction
self.score_modified_impact_base = min(1 -
(1 - confidentality_impact * confidentality_requirement)
* (1 - integrity_impact * integrity_requirement)
* (1 - availability_impact * availability_requirement), 0.915)

if scope:
self.score_modified_impact = 7.52 * (self.score_modified_impact_base - 0.029) - 3.25 * (self.score_modified_impact_base - 0.02)**15
else:
self.score_modified_impact = 6.42 * self.score_modified_impact_base

if self.score_modified_impact <= 0:
return 0
elif scope:
return self.round_up(self.round_up(min(self.score_modified_impact+exploitability, 10))
* exploitability
* remediation_level
* report_confidence)
else:
return self.round_up(self.round_up(min(1.08 * (self.score_modified_impact+exploitability), 10))
* exploitability
* remediation_level
* report_confidence)

def __init__(self, vector):
# Initialization

# Initialize vector here
self.vector = {'AV':None, 'AC':None, 'PR':None, 'UI':None, 'S':None, 'C':None, 'I':None, 'A':None, 'E':'X', 'RL':'X', 'RC':'X', 'MAV':'X', 'MAC':'X', 'MPR':'X', 'MUI':'X', 'MS':'X', 'MC':'X', 'MI':'X', 'MA':'X', 'CR':'X', 'IR':'X', 'AR':'X'}
# Prep vector
self._vector = CVSS3(self.vector)
self.vector_clean = self._vector.clean_vector()

# We don't need to remove the CVSS part as it can be treated as a vector
self.vector_txt = vector
vector_list = self.vector_txt.split('/')
for vectors in vector_list:
vectorsp = vectors.split(':')
self.vector[vectorsp[0]] = vectorsp[1]
# Scores
self.base_score = self._vector.scores()[0]
self.temporal_score = self._vector.scores()[1]
self.environmental_score = self._vector.scores()[2]

# Overall score
if self.environmental_score != None:
self.score_overall = self.environmental_score
elif self.temporal_score != None:
self.score_overall = self.temporal_score
else:
self.score_overall = self.base_score

# Use the inherited calculate_overall method to calculate the overall score
self.score_overall = max(0, min(10, self.calculate_overall(['MAV', 'MAC', 'MPR', 'MUI', 'MS', 'MC', 'MI', 'MA', 'CR', 'IR', 'AR'], ['E', 'RL', 'RC'])))
# Score name
if self.score_overall >= 9.0:
self.score_name = "Critical"
elif self.score_overall >= 7.0:
Expand Down
5 changes: 1 addition & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
# Dependencies
beautifulsoup4
requests

# Testing
cvsslib
tqdm
cvss

# Setup
setuptools
Expand Down
7 changes: 0 additions & 7 deletions runtests.sh

This file was deleted.

3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
python_requires='>3.8,<4',
install_requires=[
'beautifulsoup4',
'requests'
'requests',
'cvss'
],
entry_points={
'console_scripts': [
Expand Down
Empty file removed tests/__init__.py
Empty file.
85 changes: 0 additions & 85 deletions tests/cvss2_test.py

This file was deleted.

Loading

0 comments on commit 501625f

Please sign in to comment.