Support partially-filled CVSS tables, categorize finding as 'Other' then
This commit is contained in:
parent
a354919fca
commit
201caff2fc
@ -8,6 +8,8 @@
|
||||
table.cell(str, fill: yellow, align: center)
|
||||
} else if str == "N" {
|
||||
table.cell(str, fill: lime, align: center)
|
||||
} else if str == "-" {
|
||||
table.cell(str, fill: white, align: center)
|
||||
} else {
|
||||
panic("Unknown CIA state: " + str)
|
||||
}
|
||||
|
||||
100
addons/cvss.typ
100
addons/cvss.typ
@ -33,58 +33,62 @@
|
||||
}
|
||||
|
||||
// Create a small CIA table to be included for every finding
|
||||
#let createTable(attackVector: "N", attackComplexity: "L", privilegesRequired: "N", userInteraction: "N", scope: "U", confidentiality: "H", integrity: "H", availability: "H") = {
|
||||
#let createTable(attackVector: "-", attackComplexity: "-", privilegesRequired: "-", userInteraction: "-", scope: "-", confidentiality: "-", integrity: "-", availability: "-") = {
|
||||
// Check values
|
||||
panicOnInvalid(attackVector, ("N", "A", "L", "P"))
|
||||
panicOnInvalid(attackComplexity, ("L", "H"))
|
||||
panicOnInvalid(privilegesRequired, ("N", "L", "H"))
|
||||
panicOnInvalid(userInteraction, ("N", "R"))
|
||||
panicOnInvalid(scope, ("U", "C"))
|
||||
panicOnInvalid(confidentiality, ("H", "L", "N"))
|
||||
panicOnInvalid(integrity, ("H", "L", "N"))
|
||||
panicOnInvalid(availability, ("H", "L", "N"))
|
||||
|
||||
// Calculate base result, see https://www.first.org/cvss/v3-1/specification-document#7-1-Base-Metrics-Equations
|
||||
let issLookup = ("H": 0.56, "L": 0.22, "N": 0)
|
||||
let attackVectorLookup = ("N": 0.85, "A": 0.62, "L": 0.55, "P": 0.2)
|
||||
let attackComplexityLookup = ("L": 0.77, "H": 0.44)
|
||||
let privilegesLookup = ("N": 0.85, "L": if scope == "U" { 0.62 } else { 0.68 }, "H": if scope == "U" { 0.27 } else { 0.5 })
|
||||
let userInteractionLookup = ("N": 0.85, "R": 0.62)
|
||||
let iss = 1 - ((1 - issLookup.at(confidentiality)) * (1 - issLookup.at(integrity)) * (1 - issLookup.at(availability)))
|
||||
let impact = if scope == "U" { 6.42 * iss } else { 7.52 * (iss - 0.029) - 3.25 * (iss - 0.02)}
|
||||
let exploitability = 8.22 * attackVectorLookup.at(attackVector) * attackComplexityLookup.at(attackComplexity) * privilegesLookup.at(privilegesRequired) * userInteractionLookup.at(userInteraction)
|
||||
let baseScore = if impact <= 0 { 0 } else { if scope == "U" { calc.round(calc.min(impact + exploitability, 10), digits: 1) } else { calc.round(calc.min(1.08 * (impact + exploitability), 10), digits: 1) } }
|
||||
panicOnInvalid(attackVector, ("N", "A", "L", "P", "-"))
|
||||
panicOnInvalid(attackComplexity, ("L", "H", "-"))
|
||||
panicOnInvalid(privilegesRequired, ("N", "L", "H", "-"))
|
||||
panicOnInvalid(userInteraction, ("N", "R", "-"))
|
||||
panicOnInvalid(scope, ("U", "C", "-"))
|
||||
panicOnInvalid(confidentiality, ("H", "L", "N", "-"))
|
||||
panicOnInvalid(integrity, ("H", "L", "N", "-"))
|
||||
panicOnInvalid(availability, ("H", "L", "N", "-"))
|
||||
|
||||
let status = "?"
|
||||
if baseScore >= 9.0 {
|
||||
status = "CRITICAL"
|
||||
} else if baseScore >= 7.0 {
|
||||
status = "HIGH"
|
||||
} else if baseScore >= 4.0 {
|
||||
status = "MEDIUM"
|
||||
} else if baseScore >= 0.1 {
|
||||
status = "LOW"
|
||||
if((attackVector, attackComplexity, privilegesRequired, userInteraction, scope, confidentiality, integrity, availability).find(x => x == "-") == none) {
|
||||
// Calculate base result, see https://www.first.org/cvss/v3-1/specification-document#7-1-Base-Metrics-Equations
|
||||
let issLookup = ("H": 0.56, "L": 0.22, "N": 0)
|
||||
let attackVectorLookup = ("N": 0.85, "A": 0.62, "L": 0.55, "P": 0.2)
|
||||
let attackComplexityLookup = ("L": 0.77, "H": 0.44)
|
||||
let privilegesLookup = ("N": 0.85, "L": if scope == "U" { 0.62 } else { 0.68 }, "H": if scope == "U" { 0.27 } else { 0.5 })
|
||||
let userInteractionLookup = ("N": 0.85, "R": 0.62)
|
||||
let iss = 1 - ((1 - issLookup.at(confidentiality)) * (1 - issLookup.at(integrity)) * (1 - issLookup.at(availability)))
|
||||
let impact = if scope == "U" { 6.42 * iss } else { 7.52 * (iss - 0.029) - 3.25 * (iss - 0.02)}
|
||||
let exploitability = 8.22 * attackVectorLookup.at(attackVector) * attackComplexityLookup.at(attackComplexity) * privilegesLookup.at(privilegesRequired) * userInteractionLookup.at(userInteraction)
|
||||
let baseScore = if impact <= 0 { 0 } else { if scope == "U" { calc.round(calc.min(impact + exploitability, 10), digits: 1) } else { calc.round(calc.min(1.08 * (impact + exploitability), 10), digits: 1) } }
|
||||
|
||||
if baseScore >= 9.0 {
|
||||
status = "CRITICAL"
|
||||
} else if baseScore >= 7.0 {
|
||||
status = "HIGH"
|
||||
} else if baseScore >= 4.0 {
|
||||
status = "MEDIUM"
|
||||
} else if baseScore >= 0.1 {
|
||||
status = "LOW"
|
||||
} else {
|
||||
status = "NONE"
|
||||
}
|
||||
} else {
|
||||
status = "NONE"
|
||||
// At least one value is unspecified, so this finding will be categorized as "other" and CVSS Score calculation is skipped
|
||||
status = "OTHER"
|
||||
}
|
||||
|
||||
block(
|
||||
[
|
||||
#block(
|
||||
spacing: 0.4em,
|
||||
table(
|
||||
columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr),
|
||||
align: center,
|
||||
stroke: 1pt,
|
||||
table.cell(colspan: 5)[*Exploitability Metrics*],
|
||||
table.cell(colspan: 3)[*Impact Metrics*],
|
||||
table.cell(rowspan: 2, align: bottom)[*#sym.sum*],
|
||||
[*AV*], [*AC*], [*PR*], [*UI*], [*S*], [*C*], [*I*], [*A*],
|
||||
attackVector, attackComplexity, privilegesRequired, userInteraction, scope, cia.colorize(confidentiality), cia.colorize(integrity), cia.colorize(availability), coloredCell(status),
|
||||
)
|
||||
)
|
||||
#align(right)[
|
||||
#text(
|
||||
stack(
|
||||
dir: ttb,
|
||||
table(
|
||||
columns: (1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr, 1fr),
|
||||
align: center,
|
||||
stroke: 1pt,
|
||||
table.cell(colspan: 5)[*Exploitability Metrics*],
|
||||
table.cell(colspan: 3)[*Impact Metrics*],
|
||||
table.cell(rowspan: 2, align: bottom)[*#sym.sum*],
|
||||
[*AV*], [*AC*], [*PR*], [*UI*], [*S*], [*C*], [*I*], [*A*],
|
||||
attackVector, attackComplexity, privilegesRequired, userInteraction, scope, cia.colorize(confidentiality), cia.colorize(integrity), cia.colorize(availability), coloredCell(status),
|
||||
),
|
||||
v(.25em),
|
||||
align(
|
||||
right,
|
||||
text(
|
||||
size: 10pt,
|
||||
fill: gray,
|
||||
"CVSS:3.1/AV:" + attackVector +
|
||||
@ -95,8 +99,8 @@
|
||||
"/C:" + confidentiality +
|
||||
"/I:" + integrity +
|
||||
"/A:" + availability
|
||||
)]
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
updateRiskCategoryStats(status)
|
||||
|
||||
11
findings.typ
11
findings.typ
@ -6,7 +6,16 @@
|
||||
|
||||
== Administration Interfaces reachable
|
||||
|
||||
#cvss.createTable(confidentiality: "N", integrity: "N", availability: "N")
|
||||
#cvss.createTable(
|
||||
attackVector: "N",
|
||||
attackComplexity: "L",
|
||||
privilegesRequired: "N",
|
||||
userInteraction: "N",
|
||||
scope: "U",
|
||||
confidentiality: "N",
|
||||
integrity: "N",
|
||||
availability: "N",
|
||||
)
|
||||
|
||||
=== Description
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user