162 lines
9.2 KiB
Typst
162 lines
9.2 KiB
Typst
#import "../aux/valval.typ": panicOnInvalid
|
||
#import "cia.typ"
|
||
|
||
#let isUsed = state("cvssIsUsed", false)
|
||
|
||
// Statistics, used e.g. for the management summary
|
||
#let riskCategories = (
|
||
"CRITICAL": (color: red, title: "Critical", state: state("riskCriticalStat", 0)),
|
||
"HIGH": (color: orange, title: "High", state: state("riskHighStat", 0)),
|
||
"MEDIUM": (color: yellow, title: "Medium", state: state("riskMediumStat", 0)),
|
||
"LOW": (color: lime, title: "Low", state: state("riskLowStat", 0)),
|
||
"NONE": (color: white, title: "None", state: state("riskInformativeStat", 0)),
|
||
"OTHER": (color: gray, title: "Other", state: state("riskOtherStat", 0))
|
||
)
|
||
|
||
// Function to update the statistics
|
||
#let updateRiskCategoryStats(status) = {
|
||
status = upper(status)
|
||
// check argument
|
||
panicOnInvalid(status, riskCategories.keys())
|
||
|
||
// Update status
|
||
context(riskCategories.at(status).state.update(v => v + 1))
|
||
}
|
||
|
||
// Return the table cell formatted according to its content - for the CVSS result
|
||
#let coloredCell(status) = {
|
||
status = upper(status)
|
||
// check argument
|
||
panicOnInvalid(status, riskCategories.keys())
|
||
|
||
table.cell(riskCategories.at(status).title, fill: riskCategories.at(status).color, align: center)
|
||
}
|
||
|
||
// Create a small CIA table to be included for every finding
|
||
#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", "-"))
|
||
|
||
let status = "?"
|
||
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 {
|
||
// At least one value is unspecified, so this finding will be categorized as "other" and CVSS Score calculation is skipped
|
||
status = "OTHER"
|
||
}
|
||
|
||
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 +
|
||
"/AC:" + attackComplexity +
|
||
"/PR:" + privilegesRequired +
|
||
"/UI:" + userInteraction +
|
||
"/S:" + scope +
|
||
"/C:" + confidentiality +
|
||
"/I:" + integrity +
|
||
"/A:" + availability
|
||
)
|
||
)
|
||
)
|
||
|
||
updateRiskCategoryStats(status)
|
||
isUsed.update(true)
|
||
}
|
||
|
||
#let appendix() = {
|
||
[
|
||
== Common Vulnerability Scoring System (CVSS)
|
||
|
||
The Common Vulnerability Scoring System (CVSS) provides a standardized, vendor- and platform-agnostic methodology for quantifying the technical severity of software, hardware, and firmware vulnerabilities. Its outputs deliver numerical scores that contextualize a vulnerability’s risk relative to others, facilitating consistent prioritization across diverse systems. CVSS is structured around three interdependent metric groups: *Base*, *Temporal*, and *Environmental*. The Base Score captures the inherent characteristics of a vulnerability (e.g., exploitability, impact), assigning it a severity rating under idealized conditions. Temporal Metrics dynamically adjust this base score based on time-sensitive factors like exploit availability or the existence of patches. Finally, Environmental Metrics tailor the severity assessment to an organization’s specific deployment, accounting for mitigations, criticality of affected assets, and other contextual factors unique to the environment.
|
||
|
||
The only metric group that can be calculated without deep knowledge of the environment and situation is the Base Score, as the Base Score reflects intrinsic vulnerability characteristics (e.g., exploit complexity, impact on confidentiality/integrity) and is designed to be vendor-neutral and environment-agnostic. It does not account for the specific client’s infrastructure, patch status, or operational context. Since a penetration test reports on a single target environment, the Base Score represents the objective severity of the flaw within the scope of the test (e.g., "this flaw could be exploited in this network").
|
||
|
||
The version of the Common Vulnerability Scoring System used in this report is 3.1#footnote("https://www.first.org/cvss/v3-1/user-guide").
|
||
|
||
=== Attack Vector (AV)
|
||
|
||
This metric quantifies how remotely an attacker can exploit a vulnerability, directly influencing the Base Score:
|
||
|
||
- *Network (N)*: Highest severity. Attack possible from anywhere on the internet (e.g., sending a malicious packet across routers).
|
||
- *Adjacent (A)*: Moderate severity. Exploit limited to local networks (e.g., same subnet, Bluetooth/Wi-Fi, or secure VPN).
|
||
- *Local (L)*: Lower severity. Requires local access (console/SSH) or user interaction (e.g., phishing a document).
|
||
- *Physical (P)*: Lowest severity. Requires direct physical contact (e.g., evil-maid attacks, cold boot, DMA via USB).
|
||
|
||
=== Attack Complexity (AC)
|
||
|
||
This metric quantifies the technical difficulty of exploiting a vulnerability, independent of user interaction. It directly impacts the Base Score:
|
||
|
||
- *Low (L)*: Attack is repeatable and predictable with no special conditions. Example: Exploiting a buffer overflow in a service accessible via network.
|
||
- *High (H)*: Exploit requires attacker preparation or external factors, reducing reliability. This may include:
|
||
- Gathering target-specific knowledge (e.g., configuration settings, shared secrets).
|
||
- Overcoming mitigations (e.g., race conditions, anti-exploit techniques).
|
||
- Network manipulation (e.g., man-in-the-middle attacks).
|
||
- Example: Exploiting a flaw requiring a victim’s browser to accept a malicious file.
|
||
|
||
=== Privileges Required (PR)
|
||
|
||
This metric measures the attacker’s initial access level needed to exploit a vulnerability, directly impacting the Base Score:
|
||
|
||
- *None (N)*: Highest severity. Exploitable by unauthorized attackers with no prior access (e.g., unauthenticated web attack).
|
||
- *Low (L)*: Moderate severity. Requires basic user privileges (e.g., standard account access to non-sensitive resources).
|
||
- *High (H)*: Lowest severity. Needs administrative privileges to access critical system settings/files (e.g., root/superuser access).
|
||
|
||
=== User Interaction (UI)
|
||
|
||
This metric assesses whether a vulnerability requires human involvement (beyond the attacker) to be exploited, directly influencing the Base Score:
|
||
|
||
- *None (N)*: Highest severity. Exploitable without user action (e.g., automated network attack).
|
||
- *Required (R)*: Lower severity. Requires user interaction (e.g., clicking a malicious link or installing software).
|
||
|
||
=== Scope (S)
|
||
|
||
This metric determines if a vulnerability breaches security boundaries, allowing impact on components outside its original security scope (e.g., an app exploiting a database). Directly impacts Base Score severity:
|
||
|
||
- *Unchanged (U)*: Lowest severity. Vulnerability only affects resources within the same security authority (e.g., a web app affecting its own files).
|
||
- *Changed (C)*: Highest severity. Vulnerability crosses security boundaries, impacting components under different authorities (e.g., a compromised web server accessing a database).
|
||
]
|
||
} |