From febe95f7a574bde82b83a5c3e2b2baa46b7946c2 Mon Sep 17 00:00:00 2001 From: maride Date: Mon, 24 Aug 2026 15:51:44 +0200 Subject: [PATCH] Accept CVSS strings in finding creation --- addons/addons.typ | 1 + addons/cvss.typ | 63 +++++++++++++++++++++++++++++++++++----------- addons/finding.typ | 50 ++++++++++++++++++++++++++++++++++++ findings.typ | 55 ++++++++++++++++------------------------ main.typ | 5 ++-- 5 files changed, 123 insertions(+), 51 deletions(-) create mode 100644 addons/finding.typ diff --git a/addons/addons.typ b/addons/addons.typ index a0677d7..bd7b6c7 100644 --- a/addons/addons.typ +++ b/addons/addons.typ @@ -1,4 +1,5 @@ #import "cia.typ" #import "cvss.typ" +#import "finding.typ" #import "mitre.typ" #import "tlp.typ" \ No newline at end of file diff --git a/addons/cvss.typ b/addons/cvss.typ index 69ed363..b6908c6 100644 --- a/addons/cvss.typ +++ b/addons/cvss.typ @@ -32,8 +32,7 @@ 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: "-") = { +#let score(attackVector: "-", attackComplexity: "-", privilegesRequired: "-", userInteraction: "-", scope: "-", confidentiality: "-", integrity: "-", availability: "-") = { // Check values panicOnInvalid(attackVector, ("N", "A", "L", "P", "-")) panicOnInvalid(attackComplexity, ("L", "H", "-")) @@ -44,7 +43,6 @@ 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) @@ -56,21 +54,31 @@ 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" - } + + return baseScore } else { + return -1 + } +} + +// Create a small CIA table to be included for every finding. +#let createTable(attackVector: "-", attackComplexity: "-", privilegesRequired: "-", userInteraction: "-", scope: "-", confidentiality: "-", integrity: "-", availability: "-") = { + let baseScore = score(attackVector: attackVector, attackComplexity: attackComplexity, privilegesRequired: privilegesRequired, userInteraction: userInteraction, scope: scope, confidentiality: confidentiality, integrity: integrity, availability: availability) + + 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" + } else if baseScore == -1 { // At least one value is unspecified, so this finding will be categorized as "other" and CVSS Score calculation is skipped status = "OTHER" + } else { + status = "NONE" } stack( @@ -107,6 +115,31 @@ isUsed.update(true) } +// parse eats a CVSS:3.1 string and creates the appropiate table for it +#let parse(input: str) = { + let parts = input.split("/") + if parts.len() != 9 { + panic("Invalid CVSS string: " + input + ", expected 9 parts") + } + if parts.at(0) != "CVSS:3.1" { + panic("Unsupported CVSS string version: " + parts.at(0) + ", expected 'CVSS:3.1'") + } + if not parts.at(1).starts-with("AV:") or not parts.at(2).starts-with("AC:") or not parts.at(3).starts-with("PR:") or not parts.at(4).starts-with("UI:") or not parts.at(5).starts-with("S:") or not parts.at(6).starts-with("C:") or not parts.at(7).starts-with("I:") or not parts.at(8).starts-with("A:") { + panic("Invalid CVSS string: " + input + ", expected format CVSS:3.1/AV:*/AC:*/PR:*/UI:*/S:*/C:*/I:*/A:*") + } + + return ( + "attackVector": parts.at(1).last(), + "attackComplexity": parts.at(2).last(), + "privilegesRequired": parts.at(3).last(), + "userInteraction": parts.at(4).last(), + "scope": parts.at(5).last(), + "confidentiality": parts.at(6).last(), + "integrity": parts.at(7).last(), + "availability": parts.at(8).last() + ) +} + #let appendix() = { [ == Common Vulnerability Scoring System (CVSS) diff --git a/addons/finding.typ b/addons/finding.typ new file mode 100644 index 0000000..296c11d --- /dev/null +++ b/addons/finding.typ @@ -0,0 +1,50 @@ +#import "cvss.typ" as cvssAddon + +#let findingsList = state("findingsList", ()) + +#let create( + name: str, + cvss: str, + description: content, + finding: content, + evaluation: content, + recommendation: content +) = { + findingsList.update(f => { + f.push( + [ + == #name + + #cvssAddon.createTableFromString(input: cvss) + + === Description + + #description + + === Finding + + #finding + + === Evaluation + + #evaluation + + === Recommendation + + #recommendation + + #pagebreak(weak: true) + ] + ) + f + }) +} + +#let print(sort: str) = { + context( + for f in findingsList.final() { + f + } + ) +} + diff --git a/findings.typ b/findings.typ index f616128..51bd576 100644 --- a/findings.typ +++ b/findings.typ @@ -1,44 +1,31 @@ #import "addons/cve.typ" #import "addons/cvss.typ" +#import "addons/finding.typ" #import "addons/mitre.typ" -= Findings - //////////////////////////////////////////////////////////////////////////////////////////////// -== Administration Interfaces reachable +#finding.create( + name: "Administration Interfaces reachable", + cvss: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N", + description: [ + Administrative web applications and interfaces enable the management of organizational resources, processes, and data. These applications are typically used by administrative staff and other authorized persons to perform a variety of tasks. For example, administrators can use technical administration interfaces to read runtime data from servers. + ], + finding: [ + When searching for administration interfaces, the applications `Uptime Kuma` at `https://status.ellingson-mineral.co` and `Nginx Proxy Manager` at `https://nginx.ellingson-mineral.co` were found. The URLs of the administration interfaces were found via TLS Transparency Logs#footnote[https://letsencrypt.org/docs/ct-logs/]. -#cvss.createTable( - attackVector: "N", - attackComplexity: "L", - privilegesRequired: "N", - userInteraction: "N", - scope: "U", - confidentiality: "N", - integrity: "N", - availability: "N", + Both applications have a login screen and cannot be used by unauthorized visitors. Since administration accounts are set up during the initial configuration of the applications, it was not possible to log in using default credentials. A brute force attack was not performed to check for common passwords. + + The version of `Uptime Kuma` is not specified, while the version of `Nginx Proxy Manager` is 2.11.2. This instance of `Nginx Proxy Manager` is not vulnerable to the Command Injection vulnerability referenced to as #cve.reference("2024-39935"). + + This is referenced in MITRE's ATT&CK framework as #mitre.reference(name: "External Remote Services"). + ], + evaluation: [ + The administration interfaces are not vulnerable and cannot be used without valid credentials. Because of this, the findings are considered purely informative. + ], + recommendation: [ + It should be checked whether these administration interfaces must be accessible via the Internet. Protecting the interfaces behind an additional authentication layer, such as HTTP Basic Auth, or only offering them within a protected network such as a VPN would minimize the attack surface and prevent the possible exploitation of security vulnerabilities in the administration interfaces that may be found in the future. + ] ) -=== Description - -Administrative web applications and interfaces enable the management of organizational resources, processes, and data. These applications are typically used by administrative staff and other authorized persons to perform a variety of tasks. For example, administrators can use technical administration interfaces to read runtime data from servers and ensure smooth operation. - -=== Finding - -When searching for administration interfaces, the applications `Uptime Kuma` at `https://status.ellingson-mineral.co` and `Nginx Proxy Manager` at `https://nginx.ellingson-mineral.co` were found. The URLs of the administration interfaces were found via TLS Transparency Logs#footnote[https://letsencrypt.org/docs/ct-logs/]. - -Both applications have a login screen and cannot be used by unauthorized visitors. Since administration accounts are set up during the initial configuration of the applications, it was not possible to log in using default credentials. A brute force attack was not performed to check for common passwords. - -The version of `Uptime Kuma` is not specified, while the version of `Nginx Proxy Manager` is 2.11.2. This instance of `Nginx Proxy Manager` is not vulnerable to the Command Injection vulnerability referenced to as #cve.reference("2024-39935"). - -This is referenced in MITRE's ATT&CK framework as #mitre.reference(name: "External Remote Services"). - -=== Evaluation - -The administration interfaces are not vulnerable and cannot be used without valid credentials. Because of this, the findings are considered purely informative. - -=== Recommendation - -It should be checked whether these administration interfaces must be accessible via the Internet. Protecting the interfaces behind an additional authentication layer, such as HTTP Basic Auth, or only offering them within a protected network such as a VPN would minimize the attack surface and prevent the possible exploitation of security vulnerabilities in the administration interfaces that may be found in the future. - //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/main.typ b/main.typ index e198669..706d314 100644 --- a/main.typ +++ b/main.typ @@ -3,6 +3,8 @@ #import "aux/placeholder.typ": placeholder, rawPlaceholder, panicOnPlaceholder +#include "findings.typ" + // Project-specific variables #panicOnPlaceholder.update(false) #let place = placeholder("New York") @@ -114,8 +116,7 @@ #pagebreak() // ----- Findings ----- -#include "findings.typ" -#pagebreak() +#addons.finding.print(sort: "cvss") // ----- Appendix ----- = Appendix