├── lib ├── __init__.py ├── metrics.py ├── formulas.py └── pycvss3.py ├── CREDITS ├── cvss_3.calc.py ├── README.md ├── LICENSE.md └── api_call.py /lib/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015 ToolsWatch.org 2 | # This file is part of vFeed Aggregated Vulnerability Database Community -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | **CREDITS** 2 | 3 | This file is used to state the core developers team 4 | 5 | *ToolsWatch* 6 | 7 | NJ OUCHN - @toolswatch - (Project Lead and Developer) 8 | -------------------------------------------------------------------------------- /cvss_3.calc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (C) 2015 ToolsWatch.org 3 | # This file is part of vFeed Aggregated Vulnerability Database Community 4 | __version__ = 0.3 5 | __author__ = "NJ OUCHN (@toolswatch)" 6 | 7 | import argparse 8 | from lib.pycvss3 import CVSS3 9 | 10 | if __name__ == "__main__": 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument("-v", "--version", action="version", 13 | version="pycvss3 - The CVSS v3 Python Calculator version {0} by {1} ".format(__version__, __author__)) 14 | parser.add_argument("--vector", metavar="Vector mode", type=str, 15 | help="Paste the CVSS v3 vector string (without CVSS:3.0/) ") 16 | args = parser.parse_args() 17 | 18 | if args.vector: 19 | cvss3 = CVSS3(args.vector) 20 | (cvss_base_value, cvss_base_risk_level) = cvss3.cvss_base_score() 21 | (cvss_temporal_value, cvss_temporal_risk_level) = cvss3.cvss_temporal_score() 22 | (cvss_environmental_value, cvss_environmental_risk_level) = cvss3.cvss_environmental_score() 23 | 24 | print "CVSS v3 vector:", args.vector 25 | print "\tCVSS 3 Base Score: %s | Rating : %s" % (cvss_base_value, cvss_base_risk_level) 26 | print "\tCVSS 3 Temporal Score: %s | Rating : %s" % (cvss_temporal_value, cvss_temporal_risk_level) 27 | print "\tCVSS 3 Environmental Score: %s | Rating : %s" % ( 28 | cvss_environmental_value, cvss_environmental_risk_level) 29 | -------------------------------------------------------------------------------- /lib/metrics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (C) 2015 ToolsWatch.org 3 | # This file is part of vFeed Aggregated Vulnerability Database Community 4 | 5 | 6 | class Value: 7 | def __init__(self, **entries): 8 | self.__dict__.update(entries) 9 | 10 | 11 | class Metrics: 12 | """ 13 | This class returns the metric values as defined by the CVSS v3 specification doc. https://www.first.org/cvss/specification-document 14 | I added not_defined key to base metrics to deal with the Modified Base Metrics as they have the same values 15 | as the corresponding Base Metrics 16 | """ 17 | 18 | # Base Metrics 19 | attack_vector = Value(not_defined=float(0.85), network=float(0.85), adjacent_network=float(0.62), local=float(0.55), 20 | physical=float(0.20)) 21 | attack_complexity = Value(not_defined=float(0.77), low=float(0.77), high=float(0.44)) 22 | privileges_required = Value(not_defined=float(0.85), none=float(0.85), low=float(0.62), high=float(0.27)) 23 | privileges_required_changed = Value(not_defined=float(0.85), none=float(0.85), low=float(0.68), high=float(0.50)) 24 | user_interaction = Value(not_defined=float(0.85), none=float(0.85), required=float(0.62)) 25 | cia_impact = Value(not_defined=float(0.56), high=float(0.56), low=float(0.22), none=float(0)) 26 | 27 | # Temporal Metrics 28 | exploit_code_maturity = Value(not_defined=float(1.0), high=float(1), functional=float(0.97), 29 | proof_of_concept=float(0.94), unproven=float(0.91)) 30 | remediation_level = Value(not_defined=float(1.0), unavailable=float(1), workaround=float(0.97), 31 | temporary_fix=float(0.96), official_fix=float(0.95)) 32 | report_confidence = Value(not_defined=float(1.0), confirmed=float(1), reasonable=float(0.96), unknown=float(0.92)) 33 | 34 | # Environmental Metrics 35 | cia_requirement = Value(not_defined=float(1.0), high=float(1.5), medium=float(1.0), low=float(0.50)) 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pycvss3 - Python API for the CVSS v3 2 | 3 | First.org made available the version 3 of the Common Vulnerability Scoring System (CVSS). The new system is the latest update of the universal open and standardized method for rating IT vulnerabilities and determining the urgency of response. 4 | The updated version includes enhancements such as: the promotion of consistency in scoring, the replacement of Scoring Tips in order to more clearly guide end users of CVSS, and consideration of the system in order to make it more applicable to modern concerns. More information on the standard is available at https://www.first.org/cvss. 5 | 6 | pycvss3 is Python library calculator for the newest CVSS v3 and can be invoked from scripts as API or directly from command line. The API and CLI can both display the score alongside the Qualitative Rating Scale 7 | 8 | 9 | 10 | 11 | Basic usage 12 | ============== 13 |
 
14 | ./cvss_3.calc.py --vector AV:P/AC:H/PR:H/UI:R/S:C/C:N/I:N/A:N/E:P/RL:T/RC:R/CR:L/IR:L/AR:L/MAV:P/MAC:H/MPR:H/MUI:R/MS:U/MC:N/MI:N/MA:N
15 | CVSS v3 vector: AV:P/AC:H/PR:H/UI:R/S:C/C:N/I:N/A:N/E:P/RL:T/RC:R/CR:L/IR:L/AR:L/MAV:P/MAC:H/MPR:H/MUI:R/MS:U/MC:N/MI:N/MA:N
16 |         CVSS 3 Base Score: 0.0 --> Risk Level: None
17 |         CVSS 3 Temporal Score: 0.0 --> Risk Level: None
18 |         CVSS 3 Environmental Score: 0.0 --> Risk Level: None
19 | 
20 | ./cvss_3.calc.py --vector AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:H/E:H/RL:W/RC:U/CR:H/IR:H/AR:M/MAV:L/MAC:L/MPR:H/MUI:N/MS:C/MC:N/MI:H/MA:L
21 | CVSS v3 vector: AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:H/E:H/RL:W/RC:U/CR:H/IR:H/AR:M/MAV:L/MAC:L/MPR:H/MUI:N/MS:C/MC:N/MI:H/MA:L
22 |         CVSS 3 Base Score: 5.8 | Rating : Medium
23 |         CVSS 3 Temporal Score: 5.2 | Rating : Medium
24 |         CVSS 3 Environmental Score: 7.4 | Rating : High
25 | 
26 | 27 | Calling the API 28 | ============== 29 | 30 | Edit the `api_call.py` to see how to leverage the class from your scripts. 31 | 32 | To do 33 | ============== 34 | 35 | * Clean and optimize the pycvss3 code 36 | 37 | v0.3 38 | --------- 39 | * Added the support to the Qualitative Rating scale as defined in the CVSS v3 User Guide >> https://www.first.org/cvss/user-guide 40 | * Renamed and refactored the Vector Class to CVSS3 41 | * Update the pycvss3.py to reflect the change. 42 | 43 | v0.2 44 | --------- 45 | * Added support to Environmental score. 46 | * Fixed few calculation bugs in pycvss.py class 47 | * Fixed the non_defined valued in metrics.py class 48 | 49 | v0.1 50 | --------- 51 | * Initial release with the ability to calculatec the Base and Temporal scores in compliance with the CVSS v3.0 specifications (https://www.first.org/cvss/specification-document) 52 | The environmental score will be added later despite the fact the formula is in the code and working great. 53 | * Added api_call.py to demonstrate how to invoke the class. 54 | * Added the cvss_3.calc.py command line that accepts the CVSS v3.0 vector as input. 55 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # pycvss3 Public Source License 2 | 3 | The pycvss3 software (henceforth referred to simply as "pycvss3") is dual-licensed - Copyright 2004-2015 ToolsWatch Org. 4 | 5 | Cases that include commercialization of pycvss3 require a commercial, non-free license. Otherwise, pycvss3 can be used without charge under the terms set out below. 6 | 7 | ## 1. Definitions 8 | 9 | 1.1 "License" means this document. 10 | 11 | 1.2 "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns pycvss3. 12 | 13 | 1.3 "ToolsWatch Org." means pycvss3’s core developers, an updated list of whom can be found within the CREDITS file. 14 | 15 | 16 | ## 2. Commercialization 17 | 18 | A commercial use is one intended for commercial advantage or monetary compensation. 19 | 20 | Example cases of commercialization are: 21 | 22 | * Using pycvss3 to provide commercial managed/Software-as-a-Service services. 23 | * Distributing pycvss3 as a commercial product or as part of one. 24 | * Using pycvss3 as a value added service/product. 25 | 26 | Example cases which do not require a commercial license, and thus fall under the terms set out below, include (but are not limited to): 27 | 28 | Penetration testers (or penetration testing organizations) using pycvss3 as part of their assessment toolkit. 29 | 30 | * Penetration Testing Linux Distributions including but not limited to Kali Linux, SamuraiWTF, BackBox Linux. 31 | * Using pycvss3 to calculate your own systems scoring. 32 | * Any non-commercial use of pycvss3. 33 | 34 | If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - hacker@toolswatch.org 35 | 36 | We may grant commercial licenses at no monetary cost at our own discretion if the commercial usage is deemed by the ToolsWatch Org. to significantly benefit pycvss3. 37 | 38 | Free-use Terms and Conditions; 39 | 40 | ## 3.Redistribution 41 | 42 | Redistribution is permitted under the following conditions: 43 | 44 | * Unmodified License is provided with pycvss3. 45 | * Unmodified Copyright notices are provided with pycvss3. 46 | * Does not conflict with the commercialization clause. 47 | 48 | ## 4. Copying 49 | 50 | Copying is permitted so long as it does not conflict with the Redistribution clause. 51 | 52 | ## 5. Modification 53 | 54 | Modification is permitted so long as it does not conflict with the Redistribution clause. 55 | 56 | ## 6. Contributions 57 | 58 | Any Contributions assume the Contributor grants the ToolsWatch Org. the unlimited, non-exclusive right to reuse, modify and relicense the Contributor's content. 59 | 60 | ## 7. Support 61 | 62 | pycvss3 is provided under an “as is” basis and without any support, updates or maintenance. Support, updates and maintenance may be given according to the sole discretion of the ToolsWatch Org. 63 | 64 | ## 8. Disclaimer of Warranty 65 | 66 | pycvss3 is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the pycvss3 is free of defects, merchantable, fit for a particular purpose or non-infringing. 67 | 68 | ## 9. Limitation of Liability 69 | 70 | To the extent permitted under Law, pycvss3 is provided under an AS-IS basis. The ToolsWatch Org. shall never, and without any limit, be liable for any damage, cost, expense or any other payment incurred as a result of pycvss3's actions, failure, bugs and/or any other interaction between pycvss3 and end-equipment, computers, other software or any 3rd party, end-equipment, computer or services. 71 | 72 | ## 10. Disclaimer 73 | 74 | The ToolsWatch Org. accept no liability and are not responsible for any misuse or damage caused by pycvss3. -------------------------------------------------------------------------------- /api_call.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (C) 2015 ToolsWatch.org 3 | # This file is part of vFeed Aggregated Vulnerability Database Community 4 | 5 | from lib.pycvss3 import CVSS3 6 | 7 | print "Example 1" 8 | cvss3_vector = "AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N/E:H/RL:O/RC:C/CR:H/IR:H/AR:H/MAV:N/MAC:L/MPR:L/MUI:N/MS:C/MC:H/MI:H/MA:H" 9 | cvss3 = CVSS3(cvss3_vector) 10 | cvss_base_score_risk = cvss3.cvss_base_score() 11 | cvss_temporal_score_risk = cvss3.cvss_temporal_score() 12 | print "CVSS v3 vector:", cvss3_vector 13 | print "\tCVSS v3 Base Score and Risk Level", cvss_base_score_risk 14 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score_risk 15 | 16 | print "" 17 | print "Example 2" 18 | cvss3_vector = "AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H/E:U/RL:X/RC:X" 19 | cvss3 = CVSS3(cvss3_vector) 20 | cvss_base_score_risk = cvss3.cvss_base_score() 21 | cvss_temporal_score_risk = cvss3.cvss_temporal_score() 22 | print "CVSS v3 vector:", cvss3_vector 23 | print "\tCVSS v3 Base Score and Risk Level", cvss_base_score_risk 24 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score_risk 25 | 26 | print "" 27 | print "Example 3" 28 | cvss3_vector = "AV:P/AC:H/PR:H/UI:R/S:U/C:N/I:N/A:N/E:X/RL:X/RC:X" 29 | cvss3 = CVSS3(cvss3_vector) 30 | cvss_base_score_risk = cvss3.cvss_base_score() 31 | cvss_temporal_score_risk = cvss3.cvss_temporal_score() 32 | print "CVSS v3 vector:", cvss3_vector 33 | print "\tCVSS v3 Base Score and Risk Level", cvss_base_score_risk 34 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score_risk 35 | 36 | print "" 37 | print "Example 4" 38 | cvss3_vector = "AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H/E:H/RL:U/RC:C/CR:H/IR:H/AR:H/MAV:N/MAC:L/MPR:L/MUI:N/MS:C/MC:H/MI:H/MA:H" 39 | cvss3 = CVSS3(cvss3_vector) 40 | cvss_base_score_risk = cvss3.cvss_base_score() 41 | cvss_temporal_score_risk = cvss3.cvss_temporal_score() 42 | cvss_environmental_score_risk = cvss3.cvss_environmental_score() 43 | print "CVSS v3 vector:", cvss3_vector 44 | print "\tCVSS v3 Base Score:", cvss_base_score_risk 45 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score_risk 46 | print "\tCVSS 3 Environmental Score:", cvss_environmental_score_risk 47 | 48 | print "" 49 | print "Example 5" 50 | cvss3_vector = "AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H/E:H/RL:U/RC:C/CR:H/IR:H/AR:H/MAV:N/MAC:L/MPR:H/MUI:N/MS:U/MC:L/MI:H/MA:L" 51 | cvss3 = CVSS3(cvss3_vector) 52 | cvss_base_score_risk = cvss3.cvss_base_score() 53 | cvss_temporal_score_risk = cvss3.cvss_temporal_score() 54 | cvss_environmental_score_risk = cvss3.cvss_environmental_score() 55 | print "CVSS v3 vector:", cvss3_vector 56 | print "\tCVSS v3 Base Score:", cvss_base_score_risk 57 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score_risk 58 | print "\tCVSS 3 Environmental Score:", cvss_environmental_score_risk 59 | 60 | print "" 61 | print "Example 6" 62 | cvss3_vector = "AV:P/AC:H/PR:H/UI:R/S:C/C:N/I:N/A:N/E:P/RL:T/RC:R/CR:L/IR:L/AR:L/MAV:N/MAC:L/MPR:N/MUI:N/MS:C/MC:H/MI:H/MA:H" 63 | cvss3 = CVSS3(cvss3_vector) 64 | cvss_base_score_risk = cvss3.cvss_base_score() 65 | cvss_temporal_score_risk = cvss3.cvss_temporal_score() 66 | cvss_environmental_score_risk = cvss3.cvss_environmental_score() 67 | print "CVSS v3 vector:", cvss3_vector 68 | print "\tCVSS v3 Base Score:", cvss_base_score_risk 69 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score_risk 70 | print "\tCVSS 3 Environmental Score:", cvss_environmental_score_risk 71 | 72 | print "" 73 | print "Example 7" 74 | cvss3_vector = "AV:P/AC:H/PR:H/UI:R/S:C/C:N/I:N/A:N/E:P/RL:T/RC:R/CR:L/IR:L/AR:L/MAV:P/MAC:H/MPR:H/MUI:R/MS:U/MC:N/MI:N/MA:N" 75 | cvss3 = CVSS3(cvss3_vector) 76 | cvss_base_score_risk = cvss3.cvss_base_score() 77 | cvss_temporal_score_risk = cvss3.cvss_temporal_score() 78 | cvss_environmental_score_risk = cvss3.cvss_environmental_score() 79 | print "CVSS v3 vector:", cvss3_vector 80 | print "\tCVSS v3 Base Score:", cvss_base_score_risk 81 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score_risk 82 | print "\tCVSS 3 Environmental Score:", cvss_environmental_score_risk 83 | 84 | print "" 85 | print "Example 8 - Printing only scores" 86 | cvss3_vector = "AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:H/E:H/RL:W/RC:U/CR:H/IR:H/AR:M/MAV:L/MAC:L/MPR:H/MUI:N/MS:C/MC:N/MI:H/MA:L" 87 | cvss3 = CVSS3(cvss3_vector) 88 | (cvss_base_score,cvss_base_risk) = cvss3.cvss_base_score() 89 | (cvss_temporal_score,cvss_temporal_risk) = cvss3.cvss_temporal_score() 90 | (cvss_environmental_score,cvss_environmental_risk)= cvss3.cvss_environmental_score() 91 | print "CVSS v3 vector:", cvss3_vector 92 | print "\tCVSS v3 Base Score:", cvss_base_score 93 | print "\tCVSS v3 Temporal Score:", cvss_temporal_score 94 | print "\tCVSS 3 Environmental Score:", cvss_environmental_score 95 | 96 | print "" 97 | print "Example 9 - Printing only ratings" 98 | cvss3_vector = "AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:N/A:H/E:H/RL:W/RC:U/CR:H/IR:H/AR:M/MAV:L/MAC:L/MPR:H/MUI:N/MS:C/MC:N/MI:H/MA:L" 99 | cvss3 = CVSS3(cvss3_vector) 100 | (cvss_base_score,cvss_base_risk) = cvss3.cvss_base_score() 101 | (cvss_temporal_score,cvss_temporal_risk) = cvss3.cvss_temporal_score() 102 | (cvss_environmental_score,cvss_environmental_risk)= cvss3.cvss_environmental_score() 103 | print "CVSS v3 vector:", cvss3_vector 104 | print "\tCVSS v3 Base rating:", cvss_base_risk 105 | print "\tCVSS v3 Temporal rating:", cvss_temporal_risk 106 | print "\tCVSS 3 Environmental rating:", cvss_environmental_risk 107 | -------------------------------------------------------------------------------- /lib/formulas.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (C) 2015 ToolsWatch.org 3 | # This file is part of vFeed Aggregated Vulnerability Database Community 4 | 5 | import math 6 | 7 | 8 | def exploitability_sub_score(attack_vector_value, attack_complexity_value, privileges_required_value, 9 | user_interaction_value): 10 | """ 11 | :param attack_vector_value: 12 | :param attack_complexity_value: 13 | :param privileges_required_value: 14 | :param user_interaction_value: 15 | :return: the exploitability sub score required for the CVSS Base 16 | """ 17 | exploitability_sub_score_value = 8.22 * attack_vector_value * attack_complexity_value * privileges_required_value * \ 18 | user_interaction_value 19 | return exploitability_sub_score_value 20 | 21 | 22 | def exploitability_sub_score_modified(attack_vector_value_modified, attack_complexity_value_modified, 23 | privileges_required_value_modified, 24 | user_interaction_value_modified): 25 | """ 26 | :param attack_vector_value_modified: 27 | :param attack_complexity_value_modified: 28 | :param privileges_required_value_modified: 29 | :param user_interaction_value_modified: 30 | :return: The modified exploitability sub score as required by the Environmental score 31 | """ 32 | exploitability_sub_score_value_modified = 8.22 * attack_vector_value_modified * attack_complexity_value_modified * privileges_required_value_modified * \ 33 | user_interaction_value_modified 34 | return exploitability_sub_score_value_modified 35 | 36 | def impact_sub_score(availability_value, confidentiality_value, integrity_value): 37 | """ 38 | :param availability_value: 39 | :param confidentiality_value: 40 | :param integrity_value: 41 | :return: Impact sub score value as required by the Base score 42 | """ 43 | impact_sub_score_value = 1 - ((1 - confidentiality_value) * (1 - integrity_value) * (1 - availability_value)) 44 | return impact_sub_score_value 45 | 46 | 47 | def impact_sub_score_modified(availability_value_modified, confidentiality_value_modified, integrity_value_modified, 48 | confidentiality_requirement_value, integrity_requirement_value, 49 | availability_requirement_value): 50 | """ 51 | :param availability_value_modified: 52 | :param confidentiality_value_modified: 53 | :param integrity_value_modified: 54 | :param confidentiality_requirement_value: 55 | :param integrity_requirement_value: 56 | :param availability_requirement_value: 57 | :return: the modified Impact sub score as required by the Environmental score 58 | """ 59 | impact_sub_score_value_modified = min(0.915, 1 - ( 60 | 1 - confidentiality_value_modified * confidentiality_requirement_value) * ( 61 | 1 - integrity_value_modified * integrity_requirement_value) * ( 62 | 1 - availability_value_modified * availability_requirement_value)) 63 | return impact_sub_score_value_modified 64 | 65 | 66 | def cvss_base_formula(impact_sub_score_value, scope_value, exploitability_sub_score_value): 67 | """ 68 | :param impact_sub_score_value: 69 | :param scope_value: 70 | :param exploitability_sub_score_value: 71 | :return: the cvss base value 72 | """ 73 | if scope_value == "unchanged": 74 | impact_value = 6.42 * impact_sub_score_value 75 | cvss_base_value = min(10, impact_value + exploitability_sub_score_value) 76 | 77 | elif scope_value == "changed": 78 | impact_value = 7.52 * (impact_sub_score_value - 0.029) - 3.25 * math.pow( 79 | impact_sub_score_value - 0.02, 15) 80 | cvss_base_value = min(10, 1.08 * (impact_value + exploitability_sub_score_value)) 81 | 82 | if impact_sub_score_value <= 0: 83 | cvss_base_value = float(0.0) 84 | else: 85 | cvss_base_value = math.ceil(cvss_base_value * 10) / 10 86 | return cvss_base_value 87 | 88 | 89 | def cvss_temporal_formula(cvss_base_value, exploit_code_maturity_value, remediation_level_value, 90 | report_confidence_value): 91 | """ 92 | :param cvss_base_value: 93 | :param exploit_code_maturity_value: 94 | :param remediation_level_value: 95 | :param report_confidence_value: 96 | :return: the temporal score value 97 | """ 98 | cvss_temporal_value = cvss_base_value * exploit_code_maturity_value * remediation_level_value * \ 99 | report_confidence_value 100 | cvss_temporal_value = math.ceil(cvss_temporal_value * 10) / 10 101 | return cvss_temporal_value 102 | 103 | 104 | def cvss_environmental_formula(impact_sub_score_value_modified, exploitability_sub_score_value_modified, 105 | exploit_code_maturity_value, remediation_level_value, report_confidence_value, 106 | scope_value_modified): 107 | """ 108 | :param impact_sub_score_value_modified: 109 | :param exploitability_sub_score_value_modified: 110 | :param exploit_code_maturity_value: 111 | :param remediation_level_value: 112 | :param report_confidence_value: 113 | :param scope_value_modified: 114 | :return: the environmental score value 115 | """ 116 | if scope_value_modified == "unchanged": 117 | impact_value_modified = 6.42 * impact_sub_score_value_modified 118 | temp_score = min(10, impact_value_modified + exploitability_sub_score_value_modified) 119 | temp_score2 = math.ceil(temp_score * 10) / 10 120 | temp_score3 = temp_score2 * exploit_code_maturity_value * remediation_level_value * report_confidence_value 121 | 122 | else: 123 | scope_value_modified == "changed" 124 | impact_value_modified = 7.52 * (impact_sub_score_value_modified - 0.029) - 3.25 * math.pow( 125 | impact_sub_score_value_modified - 0.02, 15) 126 | temp_score = min(10, 1.08 * (impact_value_modified + exploitability_sub_score_value_modified)) 127 | temp_score2 = math.ceil(temp_score * 10) / 10 128 | temp_score3 = temp_score2 * exploit_code_maturity_value * remediation_level_value * report_confidence_value 129 | 130 | if impact_sub_score_value_modified <= 0: 131 | cvss_environmental_value = float(0.0) 132 | return cvss_environmental_value 133 | else: 134 | cvss_environmental_value = math.ceil(temp_score3 * 10) / 10 135 | return cvss_environmental_value 136 | -------------------------------------------------------------------------------- /lib/pycvss3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (C) 2015 ToolsWatch.org 3 | # This file is part of vFeed Aggregated Vulnerability Database Community 4 | 5 | from metrics import Metrics 6 | from formulas import * 7 | 8 | 9 | class CVSS3(object): 10 | def __init__(self, vector): 11 | self.vectors = vector.split('/') 12 | 13 | # case of temporal vector not set 14 | self.ecm_metric = "unset" 15 | self.rl_metric = "unset" 16 | self.rc_metric = "unset" 17 | 18 | # case of environmental metrics not set 19 | self.cr_metric = "unset" 20 | self.ir_metric = "unset" 21 | self.ar_metric = "unset" 22 | 23 | # case of modified base metrics not set 24 | self.mav_metric = "unset" 25 | self.mac_metric = "unset" 26 | self.mpr_metric = "unset" 27 | self.mui_metric = "unset" 28 | self.mc_metric = "unset" 29 | self.mi_metric = "unset" 30 | self.ma_metric = "unset" 31 | 32 | def get_vectors(self): 33 | """ Extract metrics from CVSS v3 vector format and set value of metrics to the appropriate value 34 | :return: Metrics values 35 | """ 36 | for self.vector in self.vectors: 37 | self.splitted = self.vector[0:].split(':') 38 | self.metric_vector = self.splitted[0] 39 | self.metric_value = self.splitted[1] 40 | 41 | if self.metric_vector == "AV": 42 | if self.metric_value == "N" or self.metric_value == "network": 43 | self.attack_vector_value = Metrics.attack_vector.network 44 | if self.metric_value == "A" or self.metric_value == "adjacent_network": 45 | self.attack_vector_value = Metrics.attack_vector.adjacent_network 46 | if self.metric_value == "L" or self.metric_value == "local": 47 | self.attack_vector_value = Metrics.attack_vector.local 48 | if self.metric_value == "P" or self.metric_value == "physical": 49 | self.attack_vector_value = Metrics.attack_vector.physical 50 | 51 | if self.metric_vector == "AC": 52 | if self.metric_value == "L" or self.metric_value == "low": 53 | self.attack_complexity_value = Metrics.attack_complexity.low 54 | if self.metric_value == "H" or self.metric_value == "high": 55 | self.attack_complexity_value = Metrics.attack_complexity.high 56 | 57 | if self.metric_vector == "UI": 58 | if self.metric_value == "N" or self.metric_value == "none": 59 | self.user_interaction_value = Metrics.user_interaction.none 60 | if self.metric_value == "R" or self.metric_value == "required": 61 | self.user_interaction_value = Metrics.user_interaction.required 62 | 63 | # Assigning the appropriate value to PR depending on the Scope. See the formula. 64 | if self.metric_vector == "S": 65 | self.scope_value = self.metric_value 66 | # getting the value of PR vector from the original splitted vectors 67 | self.splitted_2 = self.vectors[3:4][0].split(':') 68 | self.metric_vector = self.splitted_2[0] 69 | self.metric_value = self.splitted_2[1] 70 | 71 | if self.scope_value == "C" or self.scope_value == "changed": 72 | self.scope_value = "changed" 73 | if self.metric_vector == "PR": 74 | if self.metric_value == "N" or self.metric_value == "none": 75 | self.privileges_required_value = Metrics.privileges_required_changed.none 76 | if self.metric_value == "L" or self.metric_value == "low": 77 | self.privileges_required_value = Metrics.privileges_required_changed.low 78 | if self.metric_value == "H" or self.metric_value == "high": 79 | self.privileges_required_value = Metrics.privileges_required_changed.high 80 | else: 81 | self.scope_value = "unchanged" 82 | if self.metric_value == "N" or self.metric_value == "none": 83 | self.privileges_required_value = Metrics.privileges_required.none 84 | elif self.metric_value == "L" or self.metric_value == "low": 85 | self.privileges_required_value = Metrics.privileges_required.low 86 | elif self.metric_value == "H" or self.metric_value == "high": 87 | self.privileges_required_value = Metrics.privileges_required.high 88 | else: 89 | raise Exception, "(PR) Privileges Required metric is not correct" 90 | 91 | if self.metric_vector == "C": 92 | if self.metric_value == "L" or self.metric_value == "low": 93 | self.confidentiality_value = Metrics.cia_impact.low 94 | elif self.metric_value == "H" or self.metric_value == "high": 95 | self.confidentiality_value = Metrics.cia_impact.high 96 | elif self.metric_value == "N" or self.metric_value == "none": 97 | self.confidentiality_value = Metrics.cia_impact.none 98 | else: 99 | raise Exception, "(C) Confidentiality metric is not correct" 100 | 101 | if self.metric_vector == "I": 102 | if self.metric_value == "L" or self.metric_value == "low": 103 | self.integrity_value = Metrics.cia_impact.low 104 | elif self.metric_value == "H" or self.metric_value == "high": 105 | self.integrity_value = Metrics.cia_impact.high 106 | elif self.metric_value == "N" or self.metric_value == "none": 107 | self.integrity_value = Metrics.cia_impact.none 108 | else: 109 | raise Exception, "(I) Integrity metric is not correct" 110 | 111 | if self.metric_vector == "A": 112 | if self.metric_value == "L" or self.metric_value == "low": 113 | self.availability_value = Metrics.cia_impact.low 114 | elif self.metric_value == "H" or self.metric_value == "high": 115 | self.availability_value = Metrics.cia_impact.high 116 | elif self.metric_value == "N" or self.metric_value == "none": 117 | self.availability_value = Metrics.cia_impact.none 118 | else: 119 | raise Exception, "(A) Availability metric is not correct" 120 | 121 | if self.metric_vector == "E": 122 | self.ecm_metric = "set" 123 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 124 | self.exploit_code_maturity_value = Metrics.exploit_code_maturity.not_defined 125 | elif self.metric_value == "H" or self.metric_value == "high": 126 | self.exploit_code_maturity_value = Metrics.exploit_code_maturity.high 127 | elif self.metric_value == "F" or self.metric_value == "functional": 128 | self.exploit_code_maturity_value = Metrics.exploit_code_maturity.functional 129 | elif self.metric_value == "P" or self.metric_value == "proof_of_concept" or self.metric_value == "proof of concept": 130 | self.exploit_code_maturity_value = Metrics.exploit_code_maturity.proof_of_concept 131 | elif self.metric_value == "U" or self.metric_value == "unproven": 132 | self.exploit_code_maturity_value = Metrics.exploit_code_maturity.unproven 133 | else: 134 | if self.ecm_metric != "set": 135 | self.exploit_code_maturity_value = Metrics.exploit_code_maturity.not_defined 136 | 137 | if self.metric_vector == "RL": 138 | self.rl_metric = "set" 139 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 140 | self.remediation_level_value = Metrics.remediation_level.not_defined 141 | elif self.metric_value == "U" or self.metric_value == "unavailable": 142 | self.remediation_level_value = Metrics.remediation_level.unavailable 143 | elif self.metric_value == "W" or self.metric_value == "workaround": 144 | self.remediation_level_value = Metrics.remediation_level.workaround 145 | elif self.metric_value == "T" or self.metric_value == "temporary_fix" or self.metric_value == "temporary fix": 146 | self.remediation_level_value = Metrics.remediation_level.temporary_fix 147 | elif self.metric_value == "O" or self.metric_value == "official_fix" or self.metric_value == "official fix": 148 | self.remediation_level_value = Metrics.remediation_level.official_fix 149 | else: 150 | if self.rl_metric != "set": 151 | self.remediation_level_value = Metrics.remediation_level.not_defined 152 | 153 | if self.metric_vector == "RC": 154 | self.rc_metric = "set" 155 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 156 | self.report_confidence_value = Metrics.remediation_level.not_defined 157 | elif self.metric_value == "C" or self.metric_value == "confirmed": 158 | self.report_confidence_value = Metrics.report_confidence.confirmed 159 | elif self.metric_value == "R" or self.metric_value == "reasonable": 160 | self.report_confidence_value = Metrics.report_confidence.reasonable 161 | elif self.metric_value == "U" or self.metric_value == "unknown": 162 | self.report_confidence_value = Metrics.report_confidence.unknown 163 | else: 164 | if self.rc_metric != "set": 165 | # Assigning the default value in case 166 | self.report_confidence_value = Metrics.report_confidence.not_defined 167 | 168 | if self.metric_vector == "CR": 169 | self.cr_metric = "set" 170 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 171 | self.confidentiality_requirement_value = Metrics.cia_requirement.not_defined 172 | elif self.metric_value == "H" or self.metric_value == "high": 173 | self.confidentiality_requirement_value = Metrics.cia_requirement.high 174 | elif self.metric_value == "M" or self.metric_value == "medium": 175 | self.confidentiality_requirement_value = Metrics.cia_requirement.medium 176 | elif self.metric_value == "L" or self.metric_value == "low": 177 | self.confidentiality_requirement_value = Metrics.cia_requirement.low 178 | else: 179 | if self.cr_metric != "set": 180 | self.confidentiality_requirement_value = Metrics.cia_requirement.not_defined 181 | if self.metric_vector == "IR": 182 | self.ir_metric = "set" 183 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 184 | self.integrity_requirement_value = Metrics.cia_requirement.not_defined 185 | elif self.metric_value == "H" or self.metric_value == "high": 186 | self.integrity_requirement_value = Metrics.cia_requirement.high 187 | elif self.metric_value == "M" or self.metric_value == "medium": 188 | self.integrity_requirement_value = Metrics.cia_requirement.medium 189 | elif self.metric_value == "L" or self.metric_value == "low": 190 | self.integrity_requirement_value = Metrics.cia_requirement.low 191 | else: 192 | if self.ir_metric != "set": 193 | self.integrity_requirement_value = Metrics.cia_requirement.not_defined 194 | 195 | if self.metric_vector == "AR": 196 | self.ar_metric = "set" 197 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 198 | self.availability_requirement_value = Metrics.cia_requirement.not_defined 199 | elif self.metric_value == "H" or self.metric_value == "high": 200 | self.availability_requirement_value = Metrics.cia_requirement.high 201 | elif self.metric_value == "M" or self.metric_value == "medium": 202 | self.availability_requirement_value = Metrics.cia_requirement.medium 203 | elif self.metric_value == "L" or self.metric_value == "low": 204 | self.availability_requirement_value = Metrics.cia_requirement.low 205 | else: 206 | if self.ar_metric != "set": 207 | self.availability_requirement_value = Metrics.cia_requirement.not_defined 208 | 209 | if self.metric_vector == "MAV": 210 | self.mav_metric = "set" 211 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 212 | self.attack_vector_value_modified = Metrics.attack_vector.not_defined 213 | elif self.metric_value == "N" or self.metric_value == "network": 214 | self.attack_vector_value_modified = Metrics.attack_vector.network 215 | elif self.metric_value == "A" or self.metric_value == "adjacent_network": 216 | self.attack_vector_value_modified = Metrics.attack_vector.adjacent_network 217 | elif self.metric_value == "L" or self.metric_value == "local": 218 | self.attack_vector_value_modified = Metrics.attack_vector.local 219 | elif self.metric_value == "P" or self.metric_value == "physical": 220 | self.attack_vector_value_modified = Metrics.attack_vector.physical 221 | else: 222 | if self.mav_metric != "set": 223 | self.attack_vector_value_modified = Metrics.attack_vector.not_defined 224 | 225 | if self.metric_vector == "MAC": 226 | self.mac_metric = "set" 227 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 228 | self.attack_complexity_value_modified = Metrics.attack_complexity.not_defined 229 | elif self.metric_value == "L" or self.metric_value == "low": 230 | self.attack_complexity_value_modified = Metrics.attack_complexity.low 231 | elif self.metric_value == "H" or self.metric_value == "high": 232 | self.attack_complexity_value_modified = Metrics.attack_complexity.high 233 | else: 234 | if self.mac_metric != "set": 235 | self.attack_complexity_value_modified = Metrics.attack_complexity.not_defined 236 | 237 | # Assigning the appropriate value to MPR depending on the Modified Scope. See the formula. 238 | if self.metric_vector == "MS": 239 | self.scope_value_modified = self.metric_value 240 | # getting the value of MPR vector from the original splitted vectors 241 | self.splitted_3 = self.vectors[16:17][0].split(':') 242 | self.metric_vector = self.splitted_3[0] 243 | self.metric_value = self.splitted_3[1] 244 | if self.scope_value_modified == "C" or self.scope_value_modified == "changed": 245 | self.scope_value_modified = "changed" 246 | if self.metric_vector == "MPR": 247 | self.mpr_metric = "set" 248 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 249 | self.privileges_required_value_modified = Metrics.privileges_required_changed.not_defined 250 | elif self.metric_value == "N" or self.metric_value == "none": 251 | self.privileges_required_value_modified = Metrics.privileges_required_changed.none 252 | elif self.metric_value == "L" or self.metric_value == "low": 253 | self.privileges_required_value_modified = Metrics.privileges_required_changed.low 254 | elif self.metric_value == "H" or self.metric_value == "high": 255 | self.privileges_required_value_modified = Metrics.privileges_required_changed.high 256 | else: 257 | if self.mpr_metric != "set": 258 | self.privileges_required_value_modified = Metrics.privileges_required_changed.not_defined 259 | else: 260 | self.scope_value_modified = "unchanged" 261 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 262 | self.privileges_required_value_modified = Metrics.privileges_required.not_defined 263 | elif self.metric_value == "N" or self.metric_value == "none": 264 | self.privileges_required_value_modified = Metrics.privileges_required.none 265 | elif self.metric_value == "L" or self.metric_value == "low": 266 | self.privileges_required_value_modified = Metrics.privileges_required.low 267 | elif self.metric_value == "H" or self.metric_value == "high": 268 | self.privileges_required_value_modified = Metrics.privileges_required.high 269 | else: 270 | raise Exception, "(MPR) Modified Privileges Required metric is not correct" 271 | 272 | if self.metric_vector == "MUI": 273 | self.mui_metric = "set" 274 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 275 | self.user_interaction_value_modified = Metrics.user_interaction.not_defined 276 | elif self.metric_value == "N" or self.metric_value == "none": 277 | self.user_interaction_value_modified = Metrics.user_interaction.none 278 | elif self.metric_value == "R" or self.metric_value == "required": 279 | self.user_interaction_value_modified = Metrics.user_interaction.required 280 | else: 281 | if self.mui_metric != "set": 282 | self.user_interaction_value_modified = Metrics.user_interaction.not_defined 283 | 284 | if self.metric_vector == "MC": 285 | self.mc_metric = "set" 286 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 287 | self.confidentiality_value_modified = Metrics.cia_impact.not_defined 288 | elif self.metric_value == "L" or self.metric_value == "low": 289 | self.confidentiality_value_modified = Metrics.cia_impact.low 290 | elif self.metric_value == "H" or self.metric_value == "high": 291 | self.confidentiality_value_modified = Metrics.cia_impact.high 292 | elif self.metric_value == "N" or self.metric_value == "none": 293 | self.confidentiality_value_modified = Metrics.cia_impact.none 294 | else: 295 | if self.mc_metric != "set": 296 | self.confidentiality_value_modified = Metrics.cia_impact.not_defined 297 | 298 | if self.metric_vector == "MI": 299 | self.mi_metric = "set" 300 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 301 | self.integrity_value_modified = Metrics.attack_vector.not_defined 302 | elif self.metric_value == "L" or self.metric_value == "low": 303 | self.integrity_value_modified = Metrics.cia_impact.low 304 | elif self.metric_value == "H" or self.metric_value == "high": 305 | self.integrity_value_modified = Metrics.cia_impact.high 306 | elif self.metric_value == "N" or self.metric_value == "none": 307 | self.integrity_value_modified = Metrics.cia_impact.none 308 | else: 309 | if self.mi_metric != "set": 310 | self.integrity_value_modified = Metrics.cia_impact.not_defined 311 | 312 | if self.metric_vector == "MA": 313 | self.ma_metric = "set" 314 | if self.metric_value == "X" or self.metric_value == "not_defined" or self.metric_value == "not defined": 315 | self.availability_value_modified = Metrics.cia_impact.not_defined 316 | elif self.metric_value == "L" or self.metric_value == "low": 317 | self.availability_value_modified = Metrics.cia_impact.low 318 | elif self.metric_value == "H" or self.metric_value == "high": 319 | self.availability_value_modified = Metrics.cia_impact.high 320 | elif self.metric_value == "N" or self.metric_value == "none": 321 | self.availability_value_modified = Metrics.cia_impact.none 322 | else: 323 | if self.ma_metric != "set": 324 | self.availability_value_modified = Metrics.cia_impact.not_defined 325 | 326 | return 327 | 328 | def cvss_base_score(self): 329 | """ call the CVSS v3 Base (in order exploitability then impact then base). 330 | :return: the CVSS v3 Base score value with its risk level 331 | """ 332 | self.get_vectors() 333 | self.exploitability_sub_score_value = exploitability_sub_score(self.attack_vector_value, 334 | self.attack_complexity_value, 335 | self.privileges_required_value, 336 | self.user_interaction_value) 337 | 338 | self.impact_sub_score_value = impact_sub_score(self.availability_value, self.confidentiality_value, 339 | self.integrity_value) 340 | 341 | self.cvss_base_score_value = cvss_base_formula(self.impact_sub_score_value, self.scope_value, 342 | self.exploitability_sub_score_value) 343 | 344 | self.cvss_base_risk_level = self.risk_score(self.cvss_base_score_value) 345 | 346 | return (self.cvss_base_score_value, self.cvss_base_risk_level) 347 | 348 | def cvss_temporal_score(self): 349 | """ call the CVSS v3 Temporal formula. The CVSS base score is required but already calculated. 350 | :return: the CVSS v3 Temporal score value with its risk level 351 | """ 352 | 353 | self.cvss_temporal_score_value = cvss_temporal_formula(self.cvss_base_score_value, 354 | self.exploit_code_maturity_value, 355 | self.remediation_level_value, 356 | self.report_confidence_value) 357 | 358 | self.cvss_temporal_risk_level = self.risk_score(self.cvss_temporal_score_value) 359 | 360 | return (self.cvss_temporal_score_value, self.cvss_temporal_risk_level) 361 | 362 | def cvss_environmental_score(self): 363 | """ call the CVSS v3 Environmental formula (in order exp. sub score, impact sub score) 364 | :return: the CVSS v3 Environmental score value with its risk level 365 | """ 366 | self.exploitability_sub_score_value_modified = exploitability_sub_score_modified( 367 | self.attack_vector_value_modified, 368 | self.attack_complexity_value_modified, 369 | self.privileges_required_value_modified, 370 | self.user_interaction_value_modified) 371 | 372 | self.impact_sub_score_value_modified = impact_sub_score_modified(self.availability_value_modified, 373 | self.confidentiality_value_modified, 374 | self.integrity_value_modified, 375 | self.confidentiality_requirement_value, 376 | self.integrity_requirement_value, 377 | self.availability_requirement_value) 378 | 379 | self.cvss_environmental_value = cvss_environmental_formula(self.impact_sub_score_value_modified, 380 | self.exploitability_sub_score_value_modified, 381 | self.exploit_code_maturity_value, 382 | self.remediation_level_value, 383 | self.report_confidence_value, 384 | self.scope_value_modified) 385 | 386 | self.cvss_environmental_risk_level = self.risk_score(self.cvss_environmental_value) 387 | 388 | return (self.cvss_environmental_value, self.cvss_environmental_risk_level) 389 | 390 | def risk_score(self, score): 391 | """ 392 | :param score: risk values 393 | :return: the qualitative risk rating values from none to critical 394 | """ 395 | if score == float(0): 396 | self.risk_level = "None" 397 | elif score >= float(0.1) and score <= float(3.9): 398 | self.risk_level = "Low" 399 | elif score >= float(4.0) and score <= float(6.9): 400 | self.risk_level = "Medium" 401 | elif score >= float(7.0) and score <= float(8.9): 402 | self.risk_level = "High" 403 | elif score >= float(9.0) and score <= float(10.0): 404 | self.risk_level = "Critical" 405 | return self.risk_level 406 | --------------------------------------------------------------------------------