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

Add v3 handling to get_cvss_score_from_base_metrics #411

Merged
merged 15 commits into from
Oct 22, 2020
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