Skip to content

Vulnerability Management Lifecycle

Vulnerability management is the continuous process of identifying, classifying, prioritising, and remediating security weaknesses before attackers exploit them. This note covers the CVE/CVSS scoring system, practical scanner usage (Nessus/OpenVAS), EPSS prioritisation, patch workflows, and metrics.


CVE - Common Vulnerabilities and Exposures

Section titled “CVE - Common Vulnerabilities and Exposures”

A CVE is a unique identifier for a publicly disclosed security vulnerability. Format: CVE-YYYY-NNNNNN.

CVE-2021-44228 → Log4Shell (Apache Log4j JNDI injection)
CVE-2017-0144 → EternalBlue (MS17-010; SMBv1 RCE - used by WannaCry)
CVE-2014-0160 → Heartbleed (OpenSSL memory disclosure)
CVE-2021-21985 → VMware vCenter RCE

CVEs are:

  • Assigned by CNAs (CVE Numbering Authorities - vendors, CERT/CC, MITRE)
  • Published to the NVD (National Vulnerability Database) with scoring and references
  • Referenced by patch advisories, scanner results, and security bulletins
Terminal window
# Look up a CVE via NVD API
curl "https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2021-44228" \
| python3 -m json.tool | head -50
# Search for CVEs affecting a specific product (API)
curl "https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=log4j&resultsPerPage=5" \
| python3 -m json.tool

A CWE classifies the type of vulnerability (the root cause), not the specific instance:

CWENameExample CVEs
CWE-79Cross-site Scripting (XSS)Many web CVEs
CWE-89SQL InjectionCommon in web apps
CWE-119Buffer OverflowCVE-2014-0160 (Heartbleed)
CWE-22Path TraversalLog file disclosure vulnerabilities
CWE-287Improper AuthenticationAuthentication bypass vulnerabilities
CWE-502Deserialization of Untrusted DataLog4Shell, Apache Shiro vuln

CWEs inform secure coding practices - knowing the CWE helps prevent the same class of bug.


CVSS - Common Vulnerability Scoring System

Section titled “CVSS - Common Vulnerability Scoring System”

CVSS provides a 0–10 score representing the severity of a vulnerability. CVSS v3.1 is the current standard.

Attack Vector (AV):
N = Network (exploitable remotely) - worst
A = Adjacent (requires adjacent network)
L = Local (requires local access)
P = Physical (requires physical access) - least severe
Attack Complexity (AC):
L = Low (no special conditions)
H = High (specific prerequisites needed)
Privileges Required (PR):
N = None (no auth needed)
L = Low (low-priv account)
H = High (admin required)
User Interaction (UI):
N = None (no user action needed for exploit)
R = Required (user must click/open something)
Scope (S):
U = Unchanged (impact stays within exploited component)
C = Changed (impact spreads to other components)
Confidentiality / Integrity / Availability Impact:
N = None
L = Low
H = High
ScoreSeverityTypical response SLA
0.0None-
0.1 – 3.9LowNext scheduled patch window
4.0 – 6.9MediumWithin 30 days
7.0 – 8.9HighWithin 1–2 weeks
9.0 – 10.0CriticalWithin 24–72 hours
Terminal window
# CVSS v3.1 vector string example
# CVE-2021-44228 (Log4Shell): CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
# Decode: Network/Low Complexity/No Privs/No User Interaction/Changed Scope/High C/I/A
# Score: 10.0 (Critical)
# Calculate CVSS score (using Python cvss library)
pip install cvss
python3 -c "
from cvss import CVSS3
c = CVSS3('CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H')
print(f'Base Score: {c.base_score}') # 10.0
print(f'Severity: {c.severities()[0]}') # Critical
"

EPSS (FIRST.org) is a statistical model that predicts the probability that a CVE will be actively exploited in the wild within the next 30 days.

MetricWhat it measures
CVSSSeverity of impact IF exploited
EPSSProbability of exploitation happening in next 30 days
CISA KEVAlready being actively exploited in the wild right now
Prioritisation decision matrix:
CVSS High + EPSS High + in CISA KEV → CRITICAL: patch immediately (hours)
CVSS High + EPSS High → patch within 24–72h
CVSS High + EPSS Low → schedule in next window (low exploitation likelihood)
CVSS Medium + EPSS High → elevate priority despite medium CVSS score
CVSS Low + EPSS Low → routine; next scheduled window
Terminal window
# Query EPSS score for a specific CVE
curl "https://api.first.org/data/v1/epss?cve=CVE-2021-44228" | python3 -m json.tool
# Response includes: epss score (0.0–1.0) and percentile
# Check if CVE is in CISA's Known Exploited Vulnerabilities catalog
curl -s "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json" \
| python3 -c "
import json, sys
data = json.load(sys.stdin)
cve = 'CVE-2021-44228'
hits = [v for v in data['vulnerabilities'] if v.get('cveID') == cve]
print(hits if hits else 'Not in KEV catalog')
"

Nessus is the industry-standard commercial scanner. You interact with it via the web UI, but understanding the concepts:

Scan types:
Basic Network Scan → discovers hosts, checks open ports, identifies services
Web Application Test → audits web apps for common vulns
Credentialed Scan → with OS credentials → checks patches, configs, installed software
Compliance Audit → checks CIS Benchmark / PCI DSS / NIST 800-53 compliance
Credentialed scan requirements:
Linux: SSH credentials + sudo rights (or root)
Windows: Admin account + WMI/RPC access + File&Printer Sharing enabled
Nessus output:
Critical / High / Medium / Low / Informational findings
Each finding links to CVE, CVSS score, solution, and plugin ID
Terminal window
# Nessus CLI (on the scanner host)
nessusd -D # start daemon
/opt/nessus/sbin/nessuscli fix --list # list config options
nessuscli scan --help # manage scans from CLI
Terminal window
# Install GVM (Greenbone Vulnerability Manager) on Debian/Ubuntu
apt install openvas
gvm-setup # first-time setup (takes 10-30 min to sync feed)
gvm-check-setup # verify everything is running
# Start all services
gvm-start
# Web UI: https://127.0.0.1:9392
# CLI interface via gvm-cli
gvm-cli socket --gmp-username admin --gmp-password admin \
--xml '<get_version/>'
# Create and launch a scan via CLI
gvm-cli socket --gmp-username admin --gmp-password admin --xml '
<create_task>
<name>Internal Network Scan</name>
<config id="daba56c8-73ec-11df-a475-002264764cea"/>
<target id="TARGET_ID"/>
</create_task>'
# Run it
gvm-cli socket --gmp-username admin ... \
--xml '<start_task task_id="TASK_ID"/>'
# Export results as PDF
gvm-cli socket --gmp-username admin ... \
--xml '<get_reports report_id="REPORT_ID" format_id="c402cc3e-b531-11e1-9163-406186ea4fc5"/>'
Terminal window
# Basic scan of a web server
nikto -h https://example.com
# Scan specific port
nikto -h example.com -p 8443
# Output to a file
nikto -h example.com -o nikto-report.html -Format html
# Scan through a proxy
nikto -h example.com -useproxy http://proxy:8080
# What Nikto checks:
# - Missing security headers (X-Frame-Options, CSP, HSTS)
# - Dangerous HTTP methods (PUT, DELETE)
# - Default credentials on common frameworks
# - Known vulnerable paths/files
# - Outdated server software versions
Terminal window
# Run all vulnerability scripts (noisy - use in controlled environments)
nmap -sV --script vuln 192.168.1.100
# Specific vulnerability checks
nmap -p 445 --script smb-vuln-ms17-010 192.168.1.0/24 # EternalBlue
nmap -p 443 --script ssl-poodle example.com # POODLE
nmap -p 443 --script ssl-heartbleed example.com # Heartbleed
nmap -p 80,443 --script http-shellshock example.com # ShellShock
nmap -p 445 --script smb-vuln-cve2017-7494 target # SambaCry
# HTTP security headers check
nmap -p 80,443 --script http-security-headers example.com

1. ASSET INVENTORY
→ Know what you have (CMDB, cloud asset inventory, network discovery)
→ You can't patch what you don't know about
Tools: nmap, AWS Config, Azure Security Center, osquery
2. VULNERABILITY DISCOVERY
→ Run scanners against all assets (credentialed where possible)
→ Subscribe to vendor security advisories
→ Monitor CVE feeds for your technology stack
Frequency: Critical assets: weekly; Others: monthly at minimum
3. TRIAGE AND PRIORITISATION
→ Apply CVSS + EPSS + CISA KEV + asset criticality
→ Validate findings (reduce false positives before assigning tickets)
→ Assign risk owners
4. REMEDIATION
→ Patch (preferred)
→ Compensating control (if patch unavailable - WAF rule, network block)
→ Accept (documented, time-limited, with senior approval)
→ Risk transfer (cyber insurance - rarely appropriate as sole response)
5. VERIFICATION
→ Re-scan after patching to confirm closure
→ Regression testing to ensure patch didn't break functionality
6. REPORTING AND METRICS
→ Mean Time to Remediate (MTTR) by severity
→ Vulnerability age (how long critical vulns live unpatched)
→ Coverage (% of assets scanned)
→ Trending: are we improving over time?
7. CONTINUOUS IMPROVEMENT
→ Update SLAs based on metrics
→ Feed findings into secure coding standards
→ Identify systemic issues (e.g., all Apache servers always behind on patches)
SeverityCriteriaMaximum remediation time
CriticalCVSS 9.0+ AND internet-exposed OR in CISA KEV24–48 hours
Critical 2CVSS 9.0+ internal only7 days
HighCVSS 7.0–8.9 + exploit in wild7 days
High 2CVSS 7.0–8.9 + no active exploit30 days
MediumCVSS 4.0–6.990 days
LowCVSS < 4.0Next scheduled patch window
AcceptedAny - formally accepted riskRe-evaluated every 90 days
Terminal window
# Example: find all Critical findings older than 7 days in a CSV export
python3 << 'EOF'
import csv
from datetime import datetime, timedelta
with open("scan-export.csv") as f:
reader = csv.DictReader(f)
overdue = []
for row in reader:
if row['Severity'] == 'Critical':
found_date = datetime.strptime(row['FirstSeen'], '%Y-%m-%d')
age = (datetime.now() - found_date).days
if age > 7:
overdue.append((row['Host'], row['CVE'], age))
for host, cve, age in sorted(overdue, key=lambda x: -x[2]):
print(f"OVERDUE {age}d: {host} → {cve}")
EOF

Terminal window
# Linux: check for pending security updates
# Debian/Ubuntu
apt list --upgradable 2>/dev/null | grep -i security
unattended-upgrade --dry-run -v # preview auto-upgrade changes
# RHEL/CentOS/Rocky
dnf check-update --security
dnf updateinfo list security all # full list with advisory IDs
dnf update --security # apply only security updates
# Check RHSA (Red Hat Security Advisory) details
dnf updateinfo info RHSA-2023:1234
# Get advisory for a specific package
dnf updateinfo list packagename
# Arch Linux
checkupdates # safe way to check without modifying system
pacman -Qu # list upgradeable packages
Terminal window
# Windows: check for missing patches
Get-HotFix | Sort-Object InstalledOn -Descending | Select-Object -First 10
# List updates that are available but not installed
$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()
$result = $searcher.Search("IsInstalled=0")
$result.Updates | Select-Object Title, @{n='KB';e={$_.KBArticleIDs}}
# Trigger Windows Update check via PowerShell
Install-Module PSWindowsUpdate -Force
Get-WindowsUpdate # list available updates
Install-WindowsUpdate -AcceptAll -AutoReboot # install all

MetricFormulaTarget
MTTR Criticalavg(remediation date – discovery date) for Critical≤ 48 hours
MTTR Highsame for High≤ 14 days
Scan coverage(assets scanned / total assets) × 100≥ 95%
Credentialed coverage(credentialed scans / total scans) × 100≥ 80%
SLA compliance ratevulns remediated within SLA / total vulns≥ 90%
Vulnerability densitytotal open vulns / total assetsTrending ↓
Critical opencount of Critical findings unpatchedShould be 0
Recurring vulnssame CVE on same host appearing after patchingShould be 0