Skip to content

Commit

Permalink
Merge pull request #411 from mattmundell/cvss-v3-calc
Browse files Browse the repository at this point in the history
Add v3 handling to get_cvss_score_from_base_metrics
  • Loading branch information
timopollmeier authored Oct 22, 2020
2 parents f37f1a5 + 9089b13 commit 55b6ed6
Show file tree
Hide file tree
Showing 6 changed files with 390 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
[#410](https://github.com/greenbone/gvm-libs/pull/410)
- Add multiple severities for nvti [#317](https://github.com/greenbone/gvm-libs/pull/317)
- Add support for new OSP element for defining alive test methods via separate subelements. [#409](https://github.com/greenbone/gvm-libs/pull/409)
- Add v3 handling to get_cvss_score_from_base_metrics [#411](https://github.com/greenbone/gvm-libs/pull/411)
- Add severity_date tag in epoch time format. [#412](https://github.com/greenbone/gvm-libs/pull/412)

### Changed
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ if (BUILD_TESTS AND NOT SKIP_SRC)

add_custom_target (tests
DEPENDS array-test alivedetection-test boreas_error-test boreas_io-test
cli-test ping-test sniffer-test util-test networking-test
cli-test cvss-test ping-test sniffer-test util-test networking-test
xmlutils-test version-test osp-test nvti-test hosts-test)

endif (BUILD_TESTS AND NOT SKIP_SRC)
Expand Down
10 changes: 10 additions & 0 deletions base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ if (BUILD_TESTS)

target_link_libraries (array-test ${CGREEN_LIBRARIES} ${GLIB_LDFLAGS} ${LINKER_HARDENING_FLAGS})

add_executable (cvss-test
EXCLUDE_FROM_ALL
cvss_tests.c)

add_test (cvss-test cvss-test)

target_include_directories (cvss-test PRIVATE ${CGREEN_INCLUDE_DIRS})

target_link_libraries (cvss-test ${CGREEN_LIBRARIES} -lm ${GLIB_LDFLAGS} ${LINKER_HARDENING_FLAGS})

add_executable (networking-test
EXCLUDE_FROM_ALL
networking_tests.c)
Expand Down
231 changes: 226 additions & 5 deletions base/cvss.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,23 @@
* @file
* @brief CVSS utility functions
*
* This file contains utility functions for handling CVSS.
* Namels a calculator for the CVSS base score from a CVSS base
* vector.
* This file contains utility functions for handling CVSS v2 and v3.
* get_cvss_score_from_base_metrics calculates the CVSS base score from a CVSS
* base vector.
*
* CVSS v3.1:
*
* See equations at https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator and
* constants at https://www.first.org/cvss/v3.1/specification-document (section
* 7.4. Metric Values).
*
* CVSS v3.0:
*
* See equations at https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator and
* constants at https://www.first.org/cvss/v3.0/specification-document (section
* 8.4. Metric Levels).
*
* CVSS v2:
*
* The base equation is the foundation of CVSS scoring. The base equation is:
* BaseScore6
Expand Down Expand Up @@ -63,8 +77,16 @@
*/

#include <glib.h>
#include <math.h>
#include <string.h>

/* Static Headers. */

static double
get_cvss_score_from_base_metrics_v3 (const char *);

/* CVSS v2. */

// clang-format off
/**
* @brief AccessVector (AV) Constants.
Expand Down Expand Up @@ -344,11 +366,16 @@ get_cvss_score_from_base_metrics (const char *cvss_str)
struct cvss cvss;
char *token, *base_str, *base_metrics;

memset (&cvss, 0x00, sizeof (struct cvss));

if (cvss_str == NULL)
return -1.0;

if (g_str_has_prefix (cvss_str, "CVSS:3.1/")
|| g_str_has_prefix (cvss_str, "CVSS:3.0/"))
return get_cvss_score_from_base_metrics_v3 (cvss_str
+ strlen ("CVSS:3.X/"));

memset (&cvss, 0x00, sizeof (struct cvss));

base_str = base_metrics = g_strdup_printf ("%s/", cvss_str);

while ((token = strchr (base_metrics, '/')) != NULL)
Expand Down Expand Up @@ -386,3 +413,197 @@ get_cvss_score_from_base_metrics (const char *cvss_str)
g_free (base_str);
return (double) -1;
}

/* CVSS v3. */

/**
* @brief Round final score as in spec.
*
* @param cvss CVSS score.
*
* @return Rounded score.
*/
static double
roundup (double cvss)
{
int trim;

/* "Roundup returns the smallest number, specified to 1 decimal place,
* that is equal to or higher than its input. For example, Roundup (4.02)
* returns 4.1; and Roundup (4.00) returns 4.0." */

/* 3.020000001 => 3.1 */
/* 3.000000001 => 3.0 */

trim = round (cvss * 100000);
if ((trim % 10000) == 0)
return trim / 100000;
return (floor (trim / 10000) + 1) / 10.0;
}

/**
* @brief Get impact.
*
* @param value Metric value.
*
* @return Impact.
*/
static double
v3_impact (const char *value)
{
if (strcasecmp (value, "N") == 0)
return 0.0;
if (strcasecmp (value, "L") == 0)
return 0.22;
if (strcasecmp (value, "H") == 0)
return 0.56;
return -1.0;
}

/**
* @brief Calculate CVSS Score.
*
* @param cvss_str Vector from which to compute score, without prefix.
*
* @return CVSS score, or -1 on error.
*/
static double
get_cvss_score_from_base_metrics_v3 (const char *cvss_str)
{
gchar **split, **point;
int scope_changed;
double impact_conf, impact_integ, impact_avail;
double vector, complexity, privilege, user;
double isc_base, impact, exploitability, base;

/* https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
* https://www.first.org/cvss/v3.1/specification-document
* https://www.first.org/cvss/v3.0/specification-document */

scope_changed = -1;
impact_conf = -1.0;
impact_integ = -1.0;
impact_avail = -1.0;
vector = -1.0;
complexity = -1.0;
privilege = -1.0;
user = -1.0;

/* AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N */

split = g_strsplit (cvss_str, "/", 0);
point = split;
while (*point)
{
/* Scope. */
if (strncasecmp ("S:", *point, 2) == 0)
{
if (strcasecmp (*point + 2, "U") == 0)
scope_changed = 0;
else if (strcasecmp (*point + 2, "C") == 0)
scope_changed = 1;
}

/* Confidentiality. */
if (strncasecmp ("C:", *point, 2) == 0)
impact_conf = v3_impact (*point + 2);

/* Integrity. */
if (strncasecmp ("I:", *point, 2) == 0)
impact_integ = v3_impact (*point + 2);

/* Availability. */
if (strncasecmp ("A:", *point, 2) == 0)
impact_avail = v3_impact (*point + 2);

/* Attack Vector. */
if (strncasecmp ("AV:", *point, 3) == 0)
{
if (strcasecmp (*point + 3, "N") == 0)
vector = 0.85;
else if (strcasecmp (*point + 3, "A") == 0)
vector = 0.62;
else if (strcasecmp (*point + 3, "L") == 0)
vector = 0.55;
else if (strcasecmp (*point + 3, "P") == 0)
vector = 0.2;
}

/* Attack Complexity. */
if (strncasecmp ("AC:", *point, 3) == 0)
{
if (strcasecmp (*point + 3, "L") == 0)
complexity = 0.77;
else if (strcasecmp (*point + 3, "H") == 0)
complexity = 0.44;
}

/* Privileges Required. */
if (strncasecmp ("PR:", *point, 3) == 0)
{
if (strcasecmp (*point + 3, "N") == 0)
privilege = 0.85;
else if (strcasecmp (*point + 3, "L") == 0)
privilege = 0.62;
else if (strcasecmp (*point + 3, "H") == 0)
privilege = 0.27;
else
privilege = -1.0;
}

/* User Interaction. */
if (strncasecmp ("UI:", *point, 3) == 0)
{
if (strcasecmp (*point + 3, "N") == 0)
user = 0.85;
else if (strcasecmp (*point + 3, "R") == 0)
user = 0.62;
}

point++;
}

g_strfreev (split);

/* All of the base metrics are required. */

if (scope_changed == -1 || impact_conf == -1.0 || impact_integ == -1.0
|| impact_avail == -1.0 || vector == -1.0 || complexity == -1.0
|| privilege == -1.0 || user == -1.0)
return -1.0;

/* Privileges Required has a special case for S:C. */

if (scope_changed && privilege == 0.62)
privilege = 0.68;
else if (scope_changed && privilege == 0.27)
privilege = 0.5;

/* Impact. */

isc_base = 1 - ((1 - impact_conf) * (1 - impact_integ) * (1 - impact_avail));

if (scope_changed)
impact = 7.52 * (isc_base - 0.029) - 3.25 * pow ((isc_base - 0.02), 15);
else
impact = 6.42 * isc_base;

if (impact <= 0)
return 0.0;

/* Exploitability. */

exploitability = 8.22 * vector * complexity * privilege * user;

/* Final. */

if (scope_changed)
base = 1.08 * (impact + exploitability);
else
base = impact + exploitability;

if (base > 10.0)
return 10.0;

return roundup (base);
}
Loading

0 comments on commit 55b6ed6

Please sign in to comment.