L02 · Threats, Vulnerabilities, and Risk
Module 1: Security Foundations · ~20 min · Lesson 2 of 71
🎯 What you'll learn
- What assets, threats, vulnerabilities, and exploits are, and how they chain together
- Why risk = likelihood × impact, and how to use it to decide what to fix first
- What CVE and CVSS are, and how to read a CVSS vector string field by field
🔥 Why this matters
In March 2017, Apache published a fix for a critical flaw in the Struts web framework (CVE-2017-5638). About two months later, attackers used that exact flaw to get into Equifax and took data on about 147 million people. The flaw was public and the patch was free, but the risk wasn't handled. (Full case study: L06.) This lesson is about the gap between "a vulnerability exists" and "this is a risk we must act on today".
1. Four words people mix up
Start with a house. You keep jewelry inside. Burglars work your street. Your back window doesn't latch. A burglar knows how to pop that latch with a credit card. Each of those has a security name:
- An asset is anything of value you need to protect: data, systems, money, credentials, reputation.
- A threat is anything that could harm an asset. When it's a person or group, it's a threat actor: a criminal gang, an insider, a bot scanning the internet. Threats can also be accidents, like a bad deploy.
- A vulnerability is a weakness a threat could use: a bug, a misconfiguration, a missing check.
- An exploit is the specific code or technique that takes advantage of a vulnerability.
THREAT ACTOR uses an through a to harm an
(who or what) ──▶ EXPLOIT ──▶ VULNERABILITY ──▶ ASSET
e.g. a botnet (the how) (the weakness) (what you value)
scanning the crafted log4j 2.14 in policy data,
internet log string policy-service AWS credentials
│
│ result
▼
IMPACT
(C, I, or A lost)
A risk needs all three: an asset worth harming, a threat able to harm it, and a vulnerability that lets the threat through. Remove one and that path carries no risk. A critical bug in a service that holds nothing and that nobody can reach is a vulnerability, but barely a risk.
Two more terms: a zero-day is a vulnerability exploited before the vendor has a fix, and a control is anything that reduces risk, such as a patch, an authorization check, or an alert.
2. Risk = likelihood × impact
Your team has 40 open security findings and one sprint. Which first? To compare them you need risk: the chance of something bad happening, combined with how bad it would be.
- Likelihood is how probable it is that a threat exploits this weakness in a given period. It depends on exposure, how easy the attack is, and whether attackers already have or use an exploit.
- Impact is how much harm results: which CIA goal fails, for how much data and money, and which regulator gets involved. (L01's FIPS 199 ratings are impact ratings.)
IMPACT ──▶
Low Medium High
┌─────────────┬─────────────┬─────────────┐
High │ MEDIUM │ HIGH │ CRITICAL │
L ├─────────────┼─────────────┼─────────────┤
I Med │ LOW │ MEDIUM │ HIGH │
K ├─────────────┼─────────────┼─────────────┤
Low │ LOW │ LOW │ MEDIUM │
└─────────────┴─────────────┴─────────────┘
fix order: top-right first, bottom-left maybe never
Most teams score each side from 1 to 5 and multiply, so 4 × 5 = 20 outranks 5 × 2 = 10. Treat the result as a sorting tool, not a measurement: multiplying two opinions isn't real arithmetic. The second deep dive shows a money-based version.
Controls work on one side or the other. A patch or an authorization check lowers likelihood. Encrypting fields or limiting what a token can read lowers impact. Whatever is left after your controls is residual risk.
3. CVE and CVSS: the public vocabulary
When a flaw is found in widely used software, everyone needs one name for it. That's CVE
(Common Vulnerabilities and Exposures): a public catalog where each vulnerability gets an ID like
CVE-2021-44228 (year plus sequence number). The program started in 1999 and is run by MITRE with
US government (CISA) funding. IDs are issued by hundreds of CNAs (CVE Numbering Authorities),
such as Apache, Microsoft, and GitHub.
A CVE ID says what the flaw is, not how bad. For that there's CVSS (Common Vulnerability Scoring System), a standard from the industry body FIRST that rates severity from 0.0 to 10.0. The NVD (National Vulnerability Database, run by NIST) adds CVSS scores and affected versions to each CVE.
| Score | Severity |
|---|---|
| 0.0 | None |
| 0.1–3.9 | Low |
| 4.0–6.9 | Medium |
| 7.0–8.9 | High |
| 9.0–10.0 | Critical |
The score is calculated from a vector string, a compact code recording the answer to each question CVSS asks. You'll see vectors in scanner output and Dependabot alerts. Most tools still show version 3.1 (2019). CVSS v4.0, released by FIRST in November 2023, drops the Scope metric, adds Attack Requirements, and splits impact between the vulnerable system and other systems. Expect to see both for years.
One warning: the CVSS base score describes the vulnerability in general, the same for everyone. It doesn't know whether you run that library, whether it's reachable, or what data sits behind it. CVSS measures severity; risk is yours to work out. (CVSS has optional environmental metrics, but most tools show only the base score.)
🧪 Worked example
Part 1: reading Log4Shell's vector, field by field
Log4Shell (CVE-2021-44228), disclosed in December 2021, was a flaw in the Java logging library
log4j 2. Logging an attacker-controlled string, such as a crafted User-Agent header, could make
it fetch and run the attacker's code (details in L36). NVD's vector:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
└──┬───┘ └────────┬────────┘ └┬┘ └────┬────┘
│ │ │ │
version EXPLOITABILITY SCOPE IMPACT
"how easy is it "how bad if
to pull off?" it works?"
| Field | Value | What it means here |
|---|---|---|
| AV, Attack Vector | N = Network | Exploitable remotely (others: Adjacent, Local, Physical) |
| AC, Attack Complexity | L = Low | No special conditions; works reliably |
| PR, Privileges Required | N = None | No account needed |
| UI, User Interaction | N = None | Nobody has to click anything |
| S, Scope | C = Changed | The flaw is in a library, but the damage reaches the whole app and host |
| C, Confidentiality | H = High | Read anything the process can: data, env vars, cloud credentials |
| I, Integrity | H = High | Change data and code |
| A, Availability | H = High | Crash or wipe the service |
In one sentence: anyone on the internet, with no account and no help from a user, can reliably take
full control of the application. That's why it scored the maximum, 10.0 Critical. Compare
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:L/I:N/A:N: a local, hard-to-trigger, admin-only, minor
information leak. It scores 1.9 Low.
Part 2: a small risk register for an API service
Now a made-up service: policy-api at api.example.com, behind Kong on EKS, storing policies in
MongoDB. A risk register lists risks with their scores, owner, and decision. Each row names the
threat, vulnerability, and asset (L = likelihood, I = impact, both 1–5):
| ID | Risk: threat → vulnerability → asset | L | I | Score | Plan |
|---|---|---|---|---|---|
| R1 | Agent edits the ID in the URL → no object-level auth check → customer PII | 4 | 5 | 20 | Ownership check this sprint (L34) |
| R2 | Bots exploit a known CVE → image not rebuilt in 9 months → pods and IAM role | 3 | 5 | 15 | Monthly rebuilds, CI scanning (L54) |
| R3 | Insider exports a collection → one broad shared Mongo user → PII | 2 | 5 | 10 | Per-service DB users (L53) |
| R4 | Scraper floods /v1/quotes → no Kong rate limit → availability |
3 | 3 | 9 | Kong rate-limiting plugin (L42) |
| R5 | AWS region outage → single region → availability | 1 | 4 | 4 | Accept; already multi-AZ |
R2 has the scariest CVSS numbers, yet R1 ranks higher: your own missing check will never get a CVE, no scanner flags it, and thousands of users can reach it today. Scores also change: if one of R2's CVEs starts being exploited in the wild, its likelihood jumps to 5.
🛠 Try it (optional, ~5 min)
- Open
https://nvd.nist.gov/vuln/detail/CVE-2021-44228, find the CVSS 3.x section, and match each vector field to the table above. Click the vector to open NVD's calculator, change AV from Network to Local, and watch the score drop. - Or use NVD's public, read-only API (needs
jq):
curl -s "https://services.nvd.nist.gov/rest/json/cves/2.0?\
cveId=CVE-2021-44228" \
| jq -r '.vulnerabilities[0].cve.metrics.cvssMetricV31[]
| [.source, .cvssData.vectorString, .cvssData.baseScore]
| @tsv'
You should see two lines: one from nvd@nist.gov and one from the CNA (shown as an ID).
Both say 10.0 here, but for other CVEs NIST and the CNA often disagree. Since early 2024 the NVD
has had a large backlog, so a recent CVE may show only the CNA's score, or "awaiting analysis".
- Take one CVE that Dependabot or a scanner flagged in a service you work on, and translate its vector into a single sentence like the Log4Shell one.
🧠 Key takeaways
- Asset = what you protect; threat = what could harm it; vulnerability = the weakness; exploit = the technique that uses it. Remove asset, threat, or vulnerability and that path has no risk.
- Risk = likelihood × impact. Controls lower one or the other; what's left is residual risk.
- A CVE names a flaw; CVSS scores its general severity from 0.0 to 10.0.
- A high CVSS score isn't automatically your top risk, and your worst risks may have no CVE at all.
📖 Words to know
| Term | Meaning |
|---|---|
| Asset | Anything of value to protect |
| Threat / threat actor | Anything that could cause harm / the person or group behind it |
| Vulnerability | A weakness a threat could use |
| Exploit | Code or a technique that uses a vulnerability |
| Zero-day | A vulnerability exploited before a fix exists |
| Control | Anything that reduces risk |
| Risk | How likely a harmful event is, combined with how bad it would be |
| Residual risk | Risk left after controls |
| CVE | Public ID for a known vulnerability |
| CNA | An organization allowed to assign CVE IDs |
| CVSS | 0.0–10.0 severity score built from a vector string |
| NVD | NIST's database adding scores and versions to CVEs |
| Risk register | A list of risks with scores, owners, and decisions |
⏭ Next time
L03: thinking like an attacker. The Cyber Kill Chain, what "attack surface" really means, and why a defender has to be right every time while an attacker only has to be right once.
CVSS is severity, not risk (EPSS and the CISA KEV catalog)
The trap
A common rule is "fix every Critical and High within 30 days." It sounds responsible, but it often fails in practice. Around 40,000 CVEs were published in 2024 alone, and a large share score 7.0 or higher. Yet only a small fraction of all CVEs (commonly estimated at around 5% or less) is ever seen exploited in the wild. Teams following CVSS alone drown in "Criticals" nobody attacks, while a 7.5 that criminals are actively using waits in the queue.
CVSS answers "how bad could this be?" It doesn't answer "is anyone actually doing it?" Two free, public data sources fill that gap.
EPSS: how likely is exploitation soon?
The EPSS (Exploit Prediction Scoring System), also published by FIRST, gives every CVE a probability from 0 to 1 that it will be exploited in the wild in the next 30 days. It's a data-driven model, retrained periodically and rescored daily, built from signals such as observed exploitation attempts, whether exploit code is public, and the type of flaw. Each score also has a percentile: 0.95 percentile means it's more likely to be exploited than 95% of all CVEs.
Most CVEs have EPSS scores well under 0.01. Log4Shell scores about 0.9999, in the top percentile of all CVEs. (Values change daily.)
KEV: is it being exploited right now?
The CISA KEV (Known Exploited Vulnerabilities) catalog, launched in November 2021, is a list of CVEs that the US Cybersecurity and Infrastructure Security Agency has confirmed are being exploited. To get in, a CVE needs reliable evidence of active exploitation and a clear fix or mitigation. Under Binding Operational Directive 22-01, US federal civilian agencies must fix KEV entries by a due date. Many private companies, including banks, use it as their "patch first" list.
KEV holds roughly 1,750 entries as of late 2026, out of more than a quarter of a million CVEs. It also flags entries known to be used in ransomware campaigns.
Putting the three together
low EPSS, not in KEV high EPSS or in KEV
┌───────────────────────┬───────────────────────┐
CVSS high │ bad if exploited, but │ FIX NOW │
(7.0–10.0) │ nobody is (yet): │ severe AND in use │
│ planned patch, watch │ │
├───────────────────────┼───────────────────────┤
CVSS low/med │ normal patch cycle │ don't skip it: │
(0.1–6.9) │ │ attackers chain these │
└───────────────────────┴───────────────────────┘
then adjust for YOUR exposure: internet-facing? what data?
The bottom-right box matters. Attackers often chain a "medium" flaw (say, an information leak) with another to get full access, so a medium in KEV beats an unexploited critical.
🧪 Worked example: triaging three findings
A scan of the example stack's EKS workloads returns three findings on the same morning (B and C are made-up but typical):
| Finding | CVSS | EPSS | KEV | Exposure | Decision |
|---|---|---|---|---|---|
A: log4j-core 2.14.1 in policy-service (CVE-2021-44228) |
10.0 | ~1.0 | Yes | Internet-facing through Kong | Emergency: patch today, block the attack pattern at Kong meanwhile |
| B: deserialization bug in a library loaded only by an offline Airflow batch job | 9.8 | 0.002 | No | No network listener, internal data only | Next regular patch cycle |
| C: path traversal in the vendor appliance at the network edge | 7.5 | 0.6 | Yes | Internet-facing | Patch today, ahead of B despite the lower CVSS |
A pure CVSS sort would put B ahead of C. Adding exploitation data and exposure flips it. That combination, severity plus threat activity plus your own environment, is what risk means in practice. (CISA also publishes SSVC, a decision tree that formalizes this kind of triage.)
🛠 Try it (~3 min, public read-only data)
# EPSS score and percentile for Log4Shell
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2021-44228" \
| jq '.data[0]'
# Is it in KEV, since when, and is ransomware use known?
curl -s "https://www.cisa.gov/sites/default/files/feeds/\
known_exploited_vulnerabilities.json" \
| jq '.vulnerabilities[]
| select(.cveID=="CVE-2021-44228")
| {cveID, dateAdded, knownRansomwareCampaignUse}'
What to look for: an epss value close to 1 and a percentile of 1.0, then a KEV entry with
dateAdded 2021-12-10 and knownRansomwareCampaignUse "Known". Try the same commands on a CVE
from one of your own scanner reports.
🧠 Extra takeaways
- CVSS rates severity; EPSS estimates the chance of exploitation in the next 30 days; KEV confirms exploitation is already happening.
- Most CVEs are never exploited, so CVSS-only patch rules waste effort and miss real threats.
- Prioritize KEV entries and high-EPSS CVEs on exposed systems first, whatever their CVSS.
- Exposure and data sensitivity in your own environment still adjust every decision.
| Term | Meaning |
|---|---|
| EPSS | FIRST's daily probability (0–1) that a CVE is exploited in the wild within 30 days |
| EPSS percentile | How a CVE's EPSS score ranks against all other CVEs |
| CISA KEV | US government catalog of CVEs confirmed to be exploited in the wild |
| BOD 22-01 | The 2021 directive requiring US federal agencies to fix KEV entries by a deadline |
| Exploit chaining | Combining several lower-severity flaws into one serious attack |
| SSVC | CISA's decision-tree method for prioritizing vulnerabilities |
Risk treatment (mitigate, transfer, accept, avoid)
A score isn't a decision
The policy-api risk register says R5 (a region outage) scores 4 and R1 (reading any policy) scores 20. Fine, but what do you actually do about each? Every risk gets one of four answers, called risk treatment options. Picking one, writing it down, and naming who decided is what turns a scary spreadsheet into a managed system.
The four options
- Mitigate (reduce): add a control that lowers likelihood or impact. Patching, an authorization check, MFA, encryption, rate limits. This is what most engineering work is.
- Transfer (share): move the financial consequence to someone else, usually through cyber insurance or a contract. You can transfer the bill, but not the accountability: regulators and customers still hold the company responsible, and the reputational harm stays with you.
- Accept: consciously decide to live with the risk because it's small or fixing it costs more than it saves. Acceptance must be explicit: a named owner with the authority to accept it (usually a business leader, not the developer), a written reason, and an expiry or review date.
- Avoid: stop doing the risky thing. Retire the endpoint, don't collect the data, don't launch the feature. The risk disappears, along with whatever value the activity had.
a risk on the register
│
is the activity worth doing at all?
│ no │ yes
▼ ▼
AVOID within the risk appetite?
stop doing it │ yes │ no
▼ ▼
ACCEPT can a control cut it
(owner + date) at a sensible cost?
│ yes │ no
▼ ▼
MITIGATE TRANSFER
(insurance,
contract)
Risk appetite is how much risk the organization is willing to carry, set by leadership. In real life the options combine: you mitigate most of a risk, insure part of what's left, and accept the rest. "Ignore" is not on the list. An unowned, undecided risk is the most dangerous kind.
Putting a price on it: ALE
For decisions involving money, such as whether a control or an insurance policy is worth it, teams use a classic formula:
- SLE (single loss expectancy): the cost of one occurrence.
- ARO (annualized rate of occurrence): how many times a year you expect it.
- ALE (annualized loss expectancy) = SLE × ARO.
Credential stuffing against a customer login API (made-up numbers)
BEFORE SLE $12,000 (fraud + investigation) × ARO 5/yr
ALE = $60,000 a year
CONTROL bot detection + step-up MFA at the gateway: $20,000/yr
AFTER SLE $12,000 × ARO 1/yr ALE = $12,000 a year
saving $48,000 − cost $20,000 = $28,000/yr ahead ──▶ MITIGATE
If the control had cost $70,000 a year, it would cost more than the whole loss it prevents, and accepting or insuring the risk would make more sense. The inputs are estimates, so treat the result as a guide to a decision, not a precise forecast. FAIR (Factor Analysis of Information Risk) is a widely used method for doing this kind of estimate more rigorously, with ranges instead of single numbers.
🧪 Worked example: treating the policy-api register
| ID | Risk (from the main lesson) | Score | Treatment | Why |
|---|---|---|---|---|
| R1 | Any agent reads any policy | 20 | Mitigate | Cheap fix, huge impact; far outside appetite |
| R2 | Known CVEs in a stale image | 15 | Mitigate | Automated rebuilds cost little |
| R3 | Insider exports a collection | 10 | Mitigate + transfer | Least-privilege DB users and audit logs; residual fraud loss covered by insurance |
| R4 | Scraper floods /v1/quotes |
9 | Mitigate | One Kong plugin |
| R5 | Region outage | 4 | Accept | Multi-region costs more than expected loss; signed by the product owner, review in 12 months |
| R6 | Old /v0/export endpoint dumps full documents; 2 callers, both migrated |
12 | Avoid | Delete the endpoint: removes the risk, keeps all value |
R6 shows the most underrated option. Deleting unused endpoints, data, and permissions is often the cheapest security work there is.
🛠 Try it (~3 min)
Find one known risk in a service you work on that nobody has decided about: an old endpoint, a stale dependency, a shared credential. Write one line for each of the four options. Which would you choose, and who in your organization would have to sign off on accepting it?
🧠 Extra takeaways
- Every risk gets one of four treatments: mitigate, transfer, accept, or avoid, often combined.
- Transferring moves the cost, not the accountability or the reputational damage.
- Acceptance needs a named owner with authority, a reason, and a review date.
- ALE = SLE × ARO helps decide whether a control is worth its cost.
- Avoiding (deleting unused things) is often the cheapest and most complete fix.
| Term | Meaning |
|---|---|
| Risk treatment | The decision about what to do with a risk: mitigate, transfer, accept, or avoid |
| Risk appetite | How much risk an organization is willing to carry, set by leadership |
| Risk acceptance | A documented, owned decision to live with a risk for a set period |
| SLE | Single loss expectancy: the cost of one occurrence |
| ARO | Annualized rate of occurrence: expected occurrences per year |
| ALE | Annualized loss expectancy: SLE × ARO |
| FAIR | A quantitative method for estimating information risk in money terms |
The quiz for this lesson and the Tests section need JavaScript.