Education › Security › Stage 4: Operations & response

Vulnerability management

CVEs, CVSS and EPSS, scanning, prioritising by exploitability, patch SLAs and proving you are actually patched.

Advanced ~30 min read Module 13 of 16

Tens of thousands of vulnerabilities are published every year. A scanner will happily list a thousand of them across your estate, and most will never be exploited by anyone. Vulnerability management is the discipline of finding the ones that matter to you, fixing them within a time that reflects their real risk, and being able to prove it. It sits between the scanning tools of the earlier modules and the incident response of the next one: done well, the emergency patch at 2 a.m. is rare, because the exposed, exploitable things were fixed on Tuesday afternoon. This module covers how vulnerabilities are identified and scored, how to prioritise with exploitability rather than raw severity, what patch SLAs look like in practice, and how to verify that what you think is patched actually is.

After this module you can
  • Read a CVE record and a CVSS vector, and explain what the score does and does not tell you
  • Prioritise with exploitability (EPSS, known-exploited lists), exposure and asset value, not severity alone
  • Run an inventory-driven scanning program across hosts, containers, code dependencies and cloud configuration
  • Set and meet remediation SLAs with owners, exceptions and escalation
  • Verify remediation and report coverage honestly

CVE, CVSS and what a score means

A CVE (Common Vulnerabilities and Exposures) identifier names one vulnerability in one product: CVE-2024-3094 is the xz backdoor, always and everywhere. Details live in the National Vulnerability Database and the vendor's advisory: affected versions, fixed versions, a description, and a CVSS score. CVSS rates the vulnerability's intrinsic severity from 0 to 10 using a vector of properties: attack vector (network, adjacent, local, physical), complexity, privileges required, user interaction, and impact on confidentiality, integrity and availability.

Reading a CVSS v3.1 vector: this one is remotely exploitable with no privileges or user interaction and total impact, which is why it scores 9.8.
text
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H   -> 9.8 Critical

AV:N  attack vector: network         (reachable remotely)
AC:L  attack complexity: low        (reliable exploit)
PR:N  privileges required: none     (unauthenticated)
UI:N  user interaction: none        (no click needed)
S:U   scope: unchanged
C:H/I:H/A:H  full impact on confidentiality, integrity, availability

CVSS describes the worst case for the vulnerability in the abstract. It does not know whether the affected code path is reachable in your deployment, whether the service is on the internet, whether an exploit exists, or whether anyone is using it. Two 9.8s can be a five-alarm fire and a non-event. That is why severity alone is a poor sorting key.

Note

Scanners also report vulnerabilities from vendor advisories and distribution trackers that are not (yet) CVEs, and distributions often backport fixes without changing the upstream version number, which confuses version-matching scanners. Prefer scanners that understand your distribution's security tracker.

Prioritise by risk, not by score

Risk is likelihood times impact, in your environment. For likelihood, use EPSS (Exploit Prediction Scoring System), a daily-updated probability that a CVE will be exploited in the next thirty days, and the Known Exploited Vulnerabilities catalogue maintained by CISA, which lists what is being exploited right now. For exposure, use your own inventory: is the vulnerable component reachable from the internet, does it face untrusted users, what would its compromise give an attacker. For impact, use asset classification: the customer database versus a build-cache server.

SignalQuestion it answersSource
CVSSHow bad is it in the worst case?NVD, vendor advisory
EPSSHow likely is exploitation soon?FIRST, updated daily
KEV listIs it being exploited now?CISA catalogue
ExposureCan an attacker reach it in our estate?Your inventory, network scans
Asset valueWhat does compromise cost us?Your data classification
A simple, explainable priority that combines the signals. The point is a stable ranking, not precision.
python
def priority(cvss: float, epss: float, on_kev: bool, internet_exposed: bool, asset_tier: int) -> float:
    """asset_tier: 1 = crown jewels ... 3 = low value. Higher result = fix sooner."""
    likelihood = 1.0 if on_kev else max(epss, 0.01)   # KEV means it is happening now
    exposure = 1.0 if internet_exposed else 0.4
    impact = cvss / 10 * {1: 1.0, 2: 0.6, 3: 0.3}[asset_tier]
    return round(likelihood * exposure * impact, 3)


# a 9.8 on an internal build server with negligible EPSS ranks below
# a 7.5 on the internet-facing API that is on the KEV list
print(priority(9.8, 0.02, False, False, 3))   # 0.002
print(priority(7.5, 0.60, True, True, 1))     # 0.75

Whatever formula you use, publish it. Engineers accept "fix this first" when they can see why, and auditors accept a documented risk-based process far more readily than a pile of unfixed criticals with no rationale.

Inventory and scanning across every layer

You cannot prioritise what you have not found, and you cannot find vulnerabilities in assets you do not know about. The inventory comes first: every host, container image, function, code repository, cloud account and SaaS integration, with an owner. Then scan each layer with the tool that understands it, on a schedule and on change.

  • Hosts and VMs: agent-based or authenticated network scanning for OS and installed packages (OpenVAS, cloud-native inspectors, commercial scanners).
  • Container images: scan in the registry continuously, not just at build; a clean image last month has new findings today (Trivy, Grype, registry-native scanning).
  • Code dependencies: SCA on every repository, stored SBOMs re-scanned daily so new advisories match existing builds.
  • Cloud configuration: misconfiguration is a vulnerability class; benchmark scanning from the cloud module.
  • Web applications: DAST on a schedule against staging, plus periodic penetration tests for what tools miss.
Re-scanning stored SBOMs is the cheapest continuous scan: no image pulls, and new advisories match old builds within a day.
bash
# nightly: scan every stored SBOM and emit JSON for the tracker
for f in sboms/*.cdx.json; do
  grype "sbom:$f" --output json --add-cpes-if-none > "results/$(basename "$f" .cdx.json).json"
done

# which images carry a specific CVE today?
grep -l 'CVE-2025-12345' results/*.json

Feed every scanner into one tracker (a purpose-built vulnerability management system, or at minimum a deduplicated database) so a vulnerability is one record with many affected assets, one owner, one status. Twelve scanner dashboards are how findings fall between the cracks.

SLAs, exceptions and the patch process

A remediation SLA turns priority into a commitment. Typical targets: known-exploited or critical-and-exposed within days, high within weeks, medium within a quarter, low in the normal maintenance cycle. The numbers matter less than three properties: they are agreed with the teams that must do the work, they are measured, and missing them escalates to someone who can reallocate time. Emergency out-of-band patching for actively exploited internet-facing vulnerabilities is a separate, faster path with its own playbook.

PriorityExampleTarget
P1: exploited or critical + exposedKEV-listed bug in the public API's framework72 hours, emergency change
P2: high, reachableAuth bypass in an internal admin tool14 days
P3: medium or unreachable highLocal privilege escalation on a locked-down host90 days
P4: lowInformational library advisoryNext scheduled update

Some findings cannot be fixed on time: the vendor has no patch, the fix breaks a dependency, the system is being retired. An exception records the risk, the compensating control (network isolation, WAF rule, monitoring), the approver and an expiry date, after which it is reviewed again. Exceptions without expiry are how a temporary decision becomes a permanent hole. Make patching itself boring: automatic OS updates in maintenance windows, image rebuilds on a cadence, dependency bots opening pull requests, and immutable infrastructure so that the fix is a redeploy rather than a login.

Verify, measure, report

"We patched it" is a claim; a rescan is evidence. Verify every P1 and P2 fix by rescanning the specific asset, and confirm the vulnerable process actually restarted — a library updated on disk while the old version stays loaded in memory is the most common false fix. Track the metrics that describe the program's health rather than its busyness: time to remediate by priority, SLA compliance by team, age of the oldest open P1, coverage (percentage of inventory scanned in the last week), and exception count and age.

Checking that a patched library is actually out of memory on a host.
bash
# processes still using deleted (replaced) shared libraries need a restart
sudo lsof -nP +c 15 2>/dev/null | grep -E 'DEL|\(deleted\)' | awk '{print $1, $2}' | sort -u

# Debian/Ubuntu: which services need restarting after the last apt upgrade
sudo needrestart -r l

Report the picture honestly: findings you know about and their ages, the inventory you cover and the gaps you do not, exceptions and their expiry. A dashboard that shows zero criticals because scanning covers a third of the estate is worse than none. The program is working when the emergency patch is rare, the backlog is small and known, and the next disclosed critical is a routine morning's work instead of a week of surprises.

Hands-on practice

Build a risk-ranked backlog from real scans

  1. Scan three container images you use (base images count) with trivy image --format json and collect the CVE ids with CVSS scores.
  2. Fetch EPSS scores for those CVEs from the public EPSS API (a single request accepts a comma-separated list) and check each against the CISA KEV catalogue download.
  3. For each image, decide whether it is internet-exposed and assign an asset tier. Run the priority function from the lesson over every finding and sort.
  4. Compare the top ten by priority with the top ten by CVSS. Write down two cases where they differ and why the priority ordering is the better one.
  5. Assign an SLA target from the lesson's table to the top ten and record an owner for each in a simple tracker (a spreadsheet is fine).
  6. Fix the highest-priority finding by updating the base image or dependency, rebuild, rescan, and confirm the CVE no longer appears.
  7. Write one exception for a finding you cannot fix now: risk, compensating control, approver, expiry date.
Cheat sheet

Vulnerability management — at a glance

Main things to focus on

  • CVSS is worst-case severity in the abstract; it does not know your exposure or whether exploits exist
  • Prioritise with EPSS, the KEV list, exposure and asset value; publish the formula
  • Inventory first, then scan every layer on change and on schedule; re-scan stored SBOMs daily
  • One tracker: a vulnerability is one record with many assets, one owner, one status
  • SLAs by priority, agreed and measured; exceptions with compensating controls and expiry
  • Verify with a rescan and a process restart check; report coverage and gaps honestly

Reading advisories

CVE-YYYY-NNNNNOne vulnerability, one identifier, everywhere
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/...Vector: how reachable, how easy, how much impact
AV:N + PR:N + UI:NRemote, unauthenticated, no click: treat as urgent if exposed
EPSS 0.0-1.0Probability of exploitation in 30 days; updated daily
KEV catalogueActively exploited; fix regardless of score
fixed version / backported fixCheck the distribution tracker, not just upstream version

Scanning by layer

trivy image --format json IMGContainer packages and app dependencies
grype sbom:FILERe-scan stored SBOMs without pulling images
pip-audit / npm audit / osv-scannerCode dependencies per repository
authenticated host scan (OpenVAS, cloud inspector)OS packages and configuration on hosts
prowler / checkovCloud and IaC misconfiguration
zap-baseline + periodic pen testWeb application, runtime and logic bugs

Process

P1 72h / P2 14d / P3 90d / P4 next cycleExample SLA ladder; agree your own
exception = risk + compensating control + approver + expiryTemporary by construction
emergency path for KEV + exposedOut-of-band change with its own playbook
auto-updates, image rebuild cadence, dependency botsMake patching routine
immutable infra: fix = redeployNo patching by hand

Verification and metrics

rescan the asset after the fixClaims are not evidence
needrestart -r l / lsof | grep deletedOld library still loaded? Restart the process
time to remediate by priorityThe core health metric
SLA compliance by team; oldest open P1Where escalation is needed
coverage % of inventory scanned this weekZero findings on a third of the estate is not zero
exception count and ageGrowing means the process is leaking

Common pitfalls

  • Sorting by CVSS and spending the quarter on unreachable 9.8s while an exploited 7.5 on the edge waits.
  • Scanning without an inventory, so the forgotten systems are also the unscanned ones.
  • Twelve scanner dashboards and no single tracker; findings fall between them.
  • Exceptions without expiry, quietly permanent.
  • Updating a library on disk and never restarting the process that loaded the old one.
  • Reporting zero criticals from a scan that covers a fraction of the estate.
Quiz

Check your understanding

5 questions · 4 to pass · answers are explained as you go. Your best score is saved on this device only.

Progress and quiz scores are saved in this browser only. Back up or restore on the hub.

Was this lesson useful? Tell me what to improve →