Vulnerability Management Program: 12 Steps, 100 Min [2026]

Microsoft’s August 2026 Patch Tuesday closed out 751 CVEs in a single release, 108 of them rated Critical, according to Computerworld’s analysis of the update. One of those, CVE-2026-68820, a WinSock AFD driver flaw, was already being exploited before the patch shipped. If your team’s entire vulnerability strategy is “wait for Patch Tuesday, then scramble,” that’s the month you find out the hard way why scrambling doesn’t scale.

A vulnerability management program replaces the scramble with a repeatable pipeline: you always know what’s exposed, which flaws attackers are actually using, and how fast each one needs to close. This tutorial builds one from scratch using free and open-source tooling, real CVE data from the August 2026 patch cycle, and scripts you can run today. Budget around 100 minutes to stand up the core toolchain across the 12 steps below; running the program day to day takes far less once it’s automated.

None of this requires an enterprise budget. Every tool in this build is free or open source: Greenbone Community Edition for scanning, the FIRST.org EPSS feed for exploitation probability, CISA’s KEV catalog for confirmed active exploitation, and Python and Ansible for the automation glue. By Step 12 you’ll have asset discovery, scanning, risk-weighted prioritization, ticket automation, wave-based patch deployment, and closed-loop verification wired together as one pipeline instead of five disconnected spreadsheets.

Google · Preferred Sources

Don't miss new tech stories on Google

Add Tech Insider once in the Google app and our stories appear in your news suggestions.

Add Now

Why Vulnerability Management Beats Chasing Patch Tuesday Alone

Patch Tuesday is a snapshot, not a strategy. It tells you what Microsoft fixed this month. It says nothing about the unpatched Linux kernel on your build servers, the exposed Exchange box a contractor spun up in March, or the fact that your DNS servers have been sitting on a vulnerable build for six weeks because nobody owns that patch queue. The Linux kernel project alone published 46 new kernel CVEs in the week of August 2-8, 2026, according to TECH VEDA’s tracking — and none of those show up on a Windows patch calendar.

Vulnerability management is the discipline of continuously finding, scoring, and closing gaps across everything you run, not just what one vendor ships on the second Tuesday of the month. It covers Windows, Linux, network appliances, SaaS misconfigurations, and container images. Done right, it turns “we’ll patch when we get to it” into “here’s exactly what ships this week, in what order, and why.”

The program in this guide borrows structure from NIST SP 800-40 Revision 4, the federal guide to enterprise patch management planning, but adapts it for a team that doesn’t have a six-person patch office. You’ll combine CVSS severity scores with EPSS exploitation-probability data and CISA’s Known Exploited Vulnerabilities signal to decide what actually matters, instead of trying to patch every Critical-rated CVE the moment it drops.

What a Vulnerability Management Program Actually Includes

A mature program has five moving parts that feed each other in a loop: asset inventory, scanning, prioritization, remediation, and verification. Skip any one of them and the whole thing degrades into guesswork. Inventory without scanning means you know what you own but not what’s broken. Scanning without prioritization buries your team in thousands of findings with no way to decide what ships first. Remediation without verification means patches “deploy” on paper but nobody confirms the fix actually landed.

This tutorial builds all five stages as a connected pipeline, not five disconnected tools. By the end, a new CVE disclosure will flow automatically from scanner finding to risk score to ticket to patch wave to verified closure, with metrics captured at every step for reporting.

Free vs Commercial Scanners: What Changes as You Scale

Every scanner in this tier does the same fundamental job: it probes a host, matches installed software and configuration against a vulnerability signature database, and reports a CVSS-scored finding. What separates the free and paid tiers is scale, automation depth, and support, not detection accuracy for common CVEs.

ScannerLicense ModelPractical Host LimitBest Fit
Greenbone Community Edition (OpenVAS)Free, open sourceUnlimited, self-hostedSmall-to-mid teams, labs, this tutorial’s build
Nessus EssentialsFree16 IPsSingle-network testing, home labs
Tenable.io / Nessus ProfessionalPaid, per-assetUnlimited (licensed)Mid-to-large orgs needing dashboards and support
Qualys VMDRPaid, per-assetUnlimited (licensed)Large enterprises, compliance-heavy environments

Start with Greenbone. It’s what this tutorial builds against, it scales to thousands of assets on modest hardware, and the process you learn transfers directly if you later migrate to a commercial platform such as those compared in our Nessus vs Qualys vs OpenVAS breakdown. The scoring, ticketing, and wave-deployment logic in Steps 5 through 9 doesn’t care which scanner produced the raw CVSS data.

How Vulnerability Management Maps to Compliance Requirements

If your organization handles payment data, health records, or enterprise SaaS customers, a vulnerability management program isn’t optional; it’s an audit line item. PCI DSS 4.0 requirement 6.3.3 requires organizations to identify and address critical vulnerabilities via a risk-ranking process within a defined timeframe, which is exactly the SLA table built in Step 6. SOC 2’s Common Criteria (CC7.1) expects evidence of ongoing vulnerability detection and monitoring, and ISO/IEC 27001’s Annex A control A.8.8 calls for timely handling of technical vulnerabilities.

Auditors for all three frameworks tend to ask the same three questions: how do you know what you own, how do you know what’s vulnerable, and can you prove remediation happened on time. The inventory in Step 1, the scan history in Step 3, and the closed-loop verification in Step 10 answer all three with exportable evidence, rather than a manually assembled spreadsheet the week before an audit.

Prerequisites: Tools, Versions, and Team Roles You Need First

Before you touch a scanner, get these in place. Mixing versions or skipping the access setup is the single biggest reason vulnerability programs stall in week two.

  • A Linux host or VM for the scanner: Ubuntu 24.04 LTS with at least 4 vCPUs and 8GB RAM for small-to-mid environments (under 500 assets).
  • Greenbone Community Edition (OpenVAS) 24.x, or Nessus Essentials 10.8+ if you prefer Tenable’s free tier (capped at 16 IPs, fine for a lab or small network).
  • Python 3.12 or newer, with the requests and pandas packages.
  • Ansible 2.17+ on a control node for patch deployment automation (works for both Linux and, via WinRM, Windows targets).
  • Docker Engine 27.x and Docker Compose v2, used to run the scanner and scoring stack as containers.
  • Free API access to the FIRST.org EPSS feed (no key required) and a downloaded copy of the CISA Known Exploited Vulnerabilities catalog.
  • A ticketing system with an API — Jira, ServiceNow, or even a free GitHub Issues repo for smaller teams.
  • Defined roles: someone who owns the scanner and feeds (usually security engineering), someone who owns each patch domain (Windows admin, Linux admin, network admin), and an executive sponsor who signs off on SLA exceptions.

You do not need a commercial vulnerability management platform to follow this tutorial. Every tool referenced here has a free tier or is fully open source. If your organization later adopts a platform like Nessus, Qualys, or a managed OpenVAS deployment, the process you build here transfers directly — you’re just swapping the scanning engine underneath the same pipeline.

Step 1-2: Build Your Asset Inventory and Deploy a Scanner

Step 1: Build a Real-Time Asset Inventory

You cannot secure what you don’t know you own. Start with an active discovery sweep across every subnet you’re responsible for, then reconcile it against your CMDB, cloud billing console, and DNS zone files. Shadow IT and forgotten test servers show up more often in this step than most teams expect.

sudo apt update && sudo apt install -y nmap
nmap -sn 10.0.0.0/16 -oG - | awk '/Up$/{print $2}' > live_hosts.txt
nmap -sV -O -iL live_hosts.txt -oX asset_scan.xml --top-ports 1000
wc -l live_hosts.txt

Feed the output into a spreadsheet or a lightweight CMDB with, at minimum, IP address, hostname, OS and version, owning team, and business criticality (tier 1 for revenue-facing systems, tier 2 for internal, tier 3 for dev/test). This tiering feeds directly into prioritization in Step 5, so don’t skip it.

Don’t stop at internal network sweeps. Pull an asset list from your cloud provider’s own inventory API too, since nmap only sees what’s reachable from wherever you run it, and plenty of cloud resources (S3 buckets, managed databases, serverless functions) never show up in a port scan at all. Run aws resourcegroupstaggingapi get-resources or the Azure/GCP equivalent alongside your network sweep and reconcile both lists into one inventory. The gap between the two is usually where the riskiest forgotten assets hide.

Step 2: Deploy and Configure a Vulnerability Scanner

Greenbone Community Edition ships as a Docker Compose stack, which is the fastest way to get a working scanner running today.

mkdir -p ~/vm-program/greenbone && cd ~/vm-program/greenbone
curl -O https://greenbone.github.io/docs/latest/_static/docker-compose.yml
docker compose --profile production pull
docker compose --profile production up -d
docker compose logs -f gvmd | grep -m1 "Ready"

The first startup downloads the NVT (Network Vulnerability Test) feed, which can take 30-45 minutes depending on connection speed. Once it’s ready, log into the web UI on port 9392, create a target group from your live_hosts.txt list, and run a “Full and Fast” scan against a small test segment before pointing it at production.

Step 3-4: Set a Scanning Cadence and Layer In KEV + EPSS Data

Step 3: Set a Continuous Scanning Cadence

A single scan is a photo. A program needs video. Run authenticated scans weekly against tier 1 and tier 2 assets, and monthly against tier 3. Trigger an out-of-cycle scan any time a vendor discloses a Critical CVE affecting software you run — the August 2026 cycle is a good example of why: a Critical, unauthenticated RCE in Windows DNS Server (CVE-2026-62878, CVSS 9.8) doesn’t wait for your next scheduled window.

Schedule the scan as a cron job calling the Greenbone GMP API, or use the built-in task scheduler in the web UI. Either way, pipe results to a consistent export path so your scoring script (Step 5) always finds fresh data.

Step 4: Layer In CISA KEV and EPSS Threat Intelligence

CVSS tells you how bad a vulnerability could be. It says nothing about whether anyone is actually exploiting it. That’s what the Exploit Prediction Scoring System (EPSS) and CISA’s Known Exploited Vulnerabilities (KEV) catalog add. Pull both automatically:

# Pull EPSS scores for a batch of CVEs (no API key required)
curl -s "https://api.first.org/data/v1/epss?cve=CVE-2026-68820,CVE-2026-62878,CVE-2026-65667" | python3 -m json.tool

# Download the current CISA KEV catalog as JSON
curl -s -A "Mozilla/5.0" "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json" \
  -o kev_catalog.json
python3 -c "import json; d=json.load(open('kev_catalog.json')); print(len(d['vulnerabilities']), 'known exploited CVEs tracked')"

Both feeds update daily. EPSS gives you a 0-1 probability that a CVE will be exploited in the next 30 days, based on the FIRST.org model. KEV is binary: is this CVE confirmed exploited in the wild, yes or no. CVE-2026-68820 landed on both lists within days of disclosure, which is exactly the signal that should override a “patch within 30 days” default SLA.

Step 5: Score and Prioritize Risk With a Python Script

This is the step most vulnerability management programs skip, and it’s the one that actually makes the program usable. Without it, your team gets a spreadsheet of 4,000 “Critical” findings and no way to know which 40 to fix this week. The script below combines your scanner’s CVSS output with EPSS probability and KEV status into a single risk score, weighted by the asset tier you set in Step 1.

import json
import requests
import pandas as pd

def fetch_epss(cve_list):
    ids = ",".join(cve_list)
    r = requests.get(f"https://api.first.org/data/v1/epss?cve={ids}", timeout=10)
    return {item["cve"]: float(item["epss"]) for item in r.json()["data"]}

def load_kev(path="kev_catalog.json"):
    with open(path) as f:
        data = json.load(f)
    return {v["cveID"] for v in data["vulnerabilities"]}

def risk_score(cvss, epss, in_kev, asset_tier):
    tier_weight = {1: 1.5, 2: 1.0, 3: 0.6}[asset_tier]
    kev_boost = 2.0 if in_kev else 1.0
    return round(cvss * (0.4 + epss) * kev_boost * tier_weight, 2)

findings = pd.read_csv("scan_findings.csv")  # columns: cve, cvss, asset, tier
epss_scores = fetch_epss(findings["cve"].unique().tolist())
kev_set = load_kev()

findings["epss"] = findings["cve"].map(epss_scores).fillna(0.0)
findings["in_kev"] = findings["cve"].isin(kev_set)
findings["risk_score"] = findings.apply(
    lambda r: risk_score(r["cvss"], r["epss"], r["in_kev"], r["tier"]), axis=1
)

ranked = findings.sort_values("risk_score", ascending=False)
ranked.to_csv("prioritized_findings.csv", index=False)
print(ranked[["cve", "asset", "cvss", "epss", "in_kev", "risk_score"]].head(15))

Run this after every scan cycle. A CVE with a modest CVSS 7.5 but confirmed KEV status and high EPSS on a tier-1 asset will now outrank a CVSS 9.8 finding on an isolated dev box, which is the correct call in practice even though it looks backwards on paper.

Step 6-7: Set SLAs and Automate Ticketing

Step 6: Set Remediation SLAs by Severity and Exploit Status

SLAs only work if they’re specific enough that nobody has to guess. Use exploit status and asset tier as the deciding factors, not raw CVSS alone.

ConditionTier 1 (Revenue-Facing)Tier 2 (Internal)Tier 3 (Dev/Test)
Confirmed KEV / actively exploited24-48 hours72 hours7 days
Critical, CVSS 9.0+, no known exploit7 days14 days30 days
High, CVSS 7.0-8.914 days30 days45 days
Medium, CVSS 4.0-6.930 days45 days90 days
Low, CVSS below 4.090 daysBest effortBest effort

These windows track the guidance several vendors converged on for the August 2026 cycle: patch network-facing services like DNS, RRAS, and Active Directory Certificate Services within 72 hours, and give client applications like Office a 1-2 week runway since they need user interaction to exploit and typically sit behind more layers of defense.

Step 7: Automate Ticketing and Ownership Assignment

Manually filing tickets for every prioritized finding doesn’t scale past your first scan cycle. Automate it so the SLA clock starts the moment a finding crosses your prioritization threshold.

import requests
import pandas as pd

JIRA_URL = "https://yourcompany.atlassian.net/rest/api/3/issue"
AUTH = ("[email protected]", "YOUR_API_TOKEN")

ranked = pd.read_csv("prioritized_findings.csv")
top_findings = ranked[ranked["risk_score"] >= 8.0]

for _, row in top_findings.iterrows():
    sla_hours = 48 if row["in_kev"] else 168
    payload = {
        "fields": {
            "project": {"key": "VULN"},
            "summary": f"[{row['cve']}] {row['asset']} — risk score {row['risk_score']}",
            "description": {
                "type": "doc", "version": 1,
                "content": [{"type": "paragraph", "content": [{"type": "text",
                    "text": f"CVSS {row['cvss']}, EPSS {row['epss']}, KEV: {row['in_kev']}. SLA: {sla_hours}h."}]}]
            },
            "issuetype": {"name": "Vulnerability"},
            "priority": {"name": "Highest" if row["in_kev"] else "High"}
        }
    }
    resp = requests.post(JIRA_URL, json=payload, auth=AUTH)
    print(row["cve"], resp.status_code)

Route tickets to the team that owns the asset (from your Step 1 inventory), not to a generic security backlog. A ticket with no clear owner is a ticket that misses its SLA.

Step 8-9: Stand Up Staging and Deploy Patches in Waves

Step 8: Stand Up a Patch Testing and Staging Pipeline

Never push a patch straight to production, even under a 48-hour SLA. Maintain a staging group that mirrors your production OS builds and critical applications at roughly a 1:10 ratio (one staging box per ten production boxes of the same build). Automated smoke tests here catch the rare patch that breaks a service before it reaches customers.

Step 9: Deploy Patches in Controlled Waves

Wave-based rollout is what turns “patch everything now” panic into a controlled process. A practical structure that several vendors recommended for the exploited WinSock driver flaw in August 2026 breaks the rollout into four windows: secure and stage the patch in the first few hours, push to a 5-10% pilot group next, then roll to non-critical production, then to everything remaining. Ansible automates this cleanly across both Linux and Windows fleets.

---
- name: Wave 1 - Pilot group patch deployment
  hosts: pilot_group
  serial: "10%"
  tasks:
    - name: Update apt cache and apply security patches (Debian/Ubuntu)
      apt:
        upgrade: safe
        update_cache: yes
      when: ansible_os_family == "Debian"

    - name: Apply Windows Update via PSWindowsUpdate
      win_updates:
        category_names:
          - SecurityUpdates
          - CriticalUpdates
        reboot: yes
      when: ansible_os_family == "Windows"

    - name: Wait for host to come back online
      wait_for_connection:
        delay: 30
        timeout: 600

Run this playbook with --limit pilot_group first, confirm no incident tickets open in the following four hours, then re-run against the wider fleet with ansible-playbook patch_wave.yml --limit production_wave2. Keep a documented rollback plan (snapshot or previous-build image) for every wave, not just the pilot.

Step 10-11: Verify Remediation and Report Metrics That Matter

Step 10: Verify Remediation With Rescans and Closed-Loop Tracking

A patch that “deployed” according to your automation tool isn’t confirmed fixed until a rescan shows the finding gone. Skipping this step is how organizations end up patched on paper and exposed in reality — a failed reboot, a service that didn’t restart, or a patch that silently failed on 3% of hosts will all hide from a deployment log but show up immediately in a follow-up scan.

# Re-scan only the hosts touched in this wave, then diff against the original finding list
gvm-cli --gmp-username admin --gmp-password "$GVM_PASS" socket \
  --xml "Wave1-Verify"

python3 - <<'EOF'
import pandas as pd
before = pd.read_csv("prioritized_findings.csv")
after = pd.read_csv("rescan_findings.csv")
still_open = before.merge(after, on=["cve", "asset"], how="inner")
print(f"{len(still_open)} of {len(before)} findings still open after Wave 1")
still_open.to_csv("verification_gap.csv", index=False)
EOF

Close the ticket automatically only when the rescan confirms the finding is gone. Anything still open after the patch window closes gets escalated, not silently rolled into next month's queue.

Step 11: Report Metrics That Executives Actually Read

Leadership doesn't need a list of CVE IDs. They need to know whether risk is trending down and whether the team is going to miss an SLA that matters. Track these four numbers monthly and put them in front of whoever owns the risk decision.

MetricFormulaHealthy Target
Mean Time to Remediate (MTTR), KEV findingsAvg(close date - detect date) for KEV-listed CVEsUnder 48 hours
SLA compliance rateFindings closed within SLA / total findings due95%+
Scan coverageAssets scanned / assets in inventory98%+ for tier 1-2
Recurrence rateFindings re-appearing after verified closure / total closedUnder 3%

A rising MTTR on KEV-listed findings is the single earliest warning sign that a program is losing ground, well before a breach makes the problem visible.

Step 12: Run Monthly Reviews and Continuous Improvement

Close the loop with a recurring 30-minute review, ideally scheduled the week after each Patch Tuesday. Pull the four metrics above, walk through any missed SLAs, and adjust. If tier-3 assets are consistently missing their 90-day window, either the tier is wrong or the ownership assignment is broken — fix the root cause, not the symptom. Keep the meeting short and data-driven: a program that needs an hour of discussion every month to explain its own numbers usually has a reporting problem, not just a remediation problem.

Use the review to spot patterns across cycles, not just last month in isolation. If the same three application teams miss SLA every single month, that's an ownership or tooling gap worth escalating to their engineering leads directly, rather than re-filing the same category of ticket indefinitely. If scan coverage keeps dropping below 98% on tier-1 assets, chase down why — a firewall rule blocking the scanner, a credential that expired, or a load balancer that only exposes a subset of backend nodes are the three most common culprits.

Revisit your asset inventory quarterly at minimum. Cloud environments especially drift fast: a team spins up a new RDS instance or an S3-fronted API and it's invisible to your scanner until someone adds it to the target list. Automating discovery against your cloud provider's asset API (AWS Config, Azure Resource Graph, or GCP Asset Inventory) closes this gap better than a manual quarterly check ever will.

Case Study: Applying This Program to the August 2026 Patch Tuesday Wave

Here's how the pipeline built above would have handled the actual August 2026 release, using the risk scoring from Step 5 against a handful of the CVEs Microsoft and outside researchers disclosed that month.

CVEComponentCVSSKEV StatusProgram SLA
CVE-2026-68820WinSock AFD driverNot fully publishedConfirmed exploited24-48 hours, tier 1
CVE-2026-62878Windows DNS Server9.8 CriticalNot yet listed72 hours, network-facing
CVE-2026-65667Microsoft Teams10.0 CriticalNot yet listed7 days, tier 1
CVE-2026-59115Microsoft Entra Provisioning9.9 CriticalNot yet listed7 days, identity infra
Linux kernel batch (46 CVEs)Kernel, various distrosMixedCase by caseRolled into next kernel maintenance window

Notice that CVE-2026-68820 outranks the two CVSS 10.0 and 9.9 findings in real urgency, purely because it's confirmed exploited. That's the exact scenario the risk-scoring script in Step 5 is built to catch — a CVSS-only sort would have buried it below two theoretically "worse" bugs that, as of the patch release, had no confirmed exploitation. According to Splashtop's breakdown of the release, roughly 43 of the month's CVEs were rated Critical remote code execution flaws, which is exactly the volume a manual, unscored triage process cannot keep up with.

Walk through what the pipeline actually does on a morning like this. The scanner completes its scheduled Tuesday-night run and exports fresh findings by 6 a.m. The Step 5 script pulls EPSS scores for every new CVE and cross-references the KEV catalog, and CVE-2026-68820 immediately jumps to the top of the queue because it's both KEV-confirmed and sitting on tier-1 domain controllers. Step 7's automation opens a ticket assigned to the Windows admin team with a 48-hour SLA stamped on it before anyone on the security team has read their email. By the time the team's 9 a.m. stand-up happens, the pilot wave from Step 9 is already staged and ready for the 5-10% rollout, and the DNS Server and Entra findings are queued behind it on their own 72-hour and 7-day clocks respectively. That's the difference between a program and a scramble: the triage decision that used to take a two-hour meeting now takes zero minutes, because the scoring already made the call.

5 Common Pitfalls That Sink Vulnerability Management Programs

  1. Scanning without authentication. Unauthenticated scans catch maybe half of what's actually wrong on a host. Set up credentialed scanning with a dedicated service account and least-privilege access, or your findings list is fiction.
  2. Treating every Critical CVE as equally urgent. A CVSS 9.8 on an air-gapped dev box is not the same emergency as a CVSS 7.5 with confirmed active exploitation on a public-facing login page. Programs that don't weight by exploit status burn their team out chasing the wrong 20%.
  3. No rollback plan before a patch wave. Patches occasionally break things — a driver update conflicts with a legacy application, a kernel patch changes network behavior. Deploying without a tested rollback path turns a routine patch into an outage.
  4. Letting the asset inventory go stale. A scanner can only assess what's on its target list. Cloud instances, shadow IT SaaS connections, and forgotten test servers accumulate risk invisibly until someone finally adds them to inventory, usually after an incident.
  5. Skipping verification and trusting the deployment log. "Patch deployed successfully" in your automation tool is not the same claim as "vulnerability confirmed closed." Reboot failures, service restart issues, and partial rollouts all hide from deployment logs but show up in a rescan.

Troubleshooting: 8 Issues and Fixes

  • Greenbone/OpenVAS feed sync stuck at 0%: Check outbound connectivity to the Greenbone feed servers on port 443; corporate proxies frequently block the sync. Set HTTPS_PROXY in the container environment and restart the gvmd service.
  • Authenticated scan shows "Login failed" on Windows targets: Confirm WinRM is enabled and the scan account isn't blocked by UAC remote restrictions. Add the account to the local Administrators group or apply the LocalAccountTokenFilterPolicy registry fix for non-domain hosts.
  • EPSS API returns empty results for valid CVE IDs: Very recently disclosed CVEs (under 24-48 hours old) sometimes haven't been scored yet. Retry the batch the next day, or fall back to CVSS-only scoring temporarily for those entries.
  • Risk scoring script throws a KeyError on the "tier" column: Your scan export doesn't include asset tier because the target group wasn't tagged during Step 1. Re-import the asset inventory with tier metadata before re-running the scan.
  • Ansible playbook hangs on "Wait for host to come back online": A patch that triggers a reboot on Windows can take longer than the default timeout, especially if a CU installs alongside a driver update. Raise the timeout parameter to 900 seconds for Windows targets specifically.
  • Jira ticket creation returns HTTP 400: The issuetype or priority field name doesn't match your Jira project's configured scheme. Pull your project's field metadata via GET /rest/api/3/issue/createmeta and match exact field names.
  • Rescan still shows a finding after a confirmed successful patch: Some checks rely on a registered file version, and package managers occasionally leave a stale version string in metadata even after the binary updates. Cross-check with a manual dpkg -l or Get-HotFix query before assuming the scanner is wrong.
  • MTTR metric looks artificially low: If tickets auto-close when a patch deploys rather than when a rescan verifies it, your MTTR is measuring deployment speed, not remediation. Fix the automation to close only on verified rescan, per Step 10.

Advanced Tips for Mature Programs

Once the base pipeline runs reliably for a few cycles, layer in these refinements. First, add a "risk acceptance" workflow for findings that genuinely can't patch within SLA — a vendor appliance awaiting a fix, a legacy system slated for decommission. Document the compensating control (network segmentation, WAF rule, monitoring) and set a review date, rather than letting the ticket silently age past due.

Second, feed your risk-scoring script with internal exploitability context, not just EPSS. If your red team or a penetration test confirms a finding is trivially exploitable in your specific environment, override the automated score. EPSS is a population-level prediction; your own testing beats it for anything you've actually verified.

Third, extend scanning into your CI/CD pipeline so vulnerable container base images and dependencies get caught before deployment, not after. A scanner that only checks running production is always one release behind; a pipeline gate catches the CVE before it ships. A free tool like Aqua Trivy or Anchore Grype drops into a build step in minutes:

# Add to your CI pipeline before the image push step
trivy image --severity CRITICAL,HIGH --exit-code 1 your-registry/app:latest
# exit-code 1 fails the build if Critical or High CVEs are found in the image layers

Feed Trivy's JSON output into the same scan_findings.csv schema used in Step 5, tagged with an asset tier of "build pipeline," and it flows through the exact same risk-scoring and ticketing logic as your infrastructure findings. One pipeline, one prioritization model, no separate container security silo.

Finally, cross-reference your KEV and EPSS pulls against your SIEM alerting so a spike in scan or exploitation attempts against a specific CVE in your environment automatically bumps that ticket's priority, closing the loop between detection and remediation instead of running them as separate workflows.

Complete Working Project: A Free Open-Source VM Stack

Putting it all together, here's the full stack you built across the 12 steps above, wired into a single Docker Compose file plus a daily orchestration script. Save this as your reference architecture.

# docker-compose.yml — vulnerability management stack
version: "3.8"
services:
  gvmd:
    image: greenbone/gvmd:stable
    volumes:
      - gvm_data:/var/lib/gvm
    ports:
      - "9392:9392"
    restart: unless-stopped

  scoring:
    build: ./scoring
    volumes:
      - ./data:/data
    environment:
      - EPSS_ENDPOINT=https://api.first.org/data/v1/epss
      - KEV_URL=https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
    depends_on:
      - gvmd

volumes:
  gvm_data:
#!/bin/bash
# daily_vm_cycle.sh — run the full pipeline end to end
set -e

echo "[1/5] Exporting latest scan results..."
gvm-cli socket --xml "" > data/scan_findings.xml
python3 scoring/xml_to_csv.py data/scan_findings.xml data/scan_findings.csv

echo "[2/5] Refreshing KEV catalog..."
curl -s -A "Mozilla/5.0" "$KEV_URL" -o data/kev_catalog.json

echo "[3/5] Scoring and prioritizing findings..."
python3 scoring/risk_score.py

echo "[4/5] Filing tickets for high-risk findings..."
python3 scoring/file_tickets.py

echo "[5/5] Verifying prior wave closures..."
python3 scoring/verify_closure.py

echo "Daily VM cycle complete: $(date)"

Wire this script into a daily cron job, and point your monthly review (Step 12) at the accumulated CSV history for trend reporting. This is the entire program: one scanner, two threat-intel feeds, a scoring script, an automated ticketing hook, and a verification loop, running on infrastructure that costs nothing beyond the compute to host it.

Frequently Asked Questions

How is vulnerability management different from patch management?

Patch management is one output of vulnerability management. Vulnerability management covers the full lifecycle: discovering assets, scanning for weaknesses, scoring and prioritizing them, and verifying fixes. Patching software is usually the remediation step, but some findings — an open port, a misconfigured cloud bucket, a weak TLS cipher — get fixed with configuration changes rather than a patch.

Do I need a paid scanner, or is OpenVAS/Greenbone enough?

Greenbone Community Edition covers the vast majority of what small and mid-sized environments need, including authenticated scanning, CVE detection, and compliance checks. Larger organizations often move to a commercial platform for better scale, dashboarding, and support SLAs, but the process built in this tutorial works identically underneath either option.

What's a realistic SLA for Critical vulnerabilities?

24-48 hours for anything confirmed on the CISA KEV list touching a tier-1 asset, 7 days for Critical CVSS findings without confirmed exploitation, and 14-30 days for High severity, scaling by asset tier. These match the windows several vendors converged on for the August 2026 Patch Tuesday cycle.

How often should I run vulnerability scans?

Weekly authenticated scans for tier-1 and tier-2 assets, monthly for tier-3, plus an out-of-cycle scan any time a Critical CVE is disclosed for software in your environment. Continuous or daily scanning is common for internet-facing infrastructure specifically.

What is EPSS and why does it matter more than CVSS alone?

The Exploit Prediction Scoring System, maintained by FIRST.org, estimates the probability a given CVE will be exploited in the next 30 days, based on real-world exploitation data. CVSS measures theoretical severity; EPSS measures actual likelihood of attack. Combining both, as the Step 5 script does, avoids wasting remediation effort on high-severity bugs nobody is targeting while catching medium-severity bugs under active attack.

Can I run this program without a dedicated security team?

Yes, at small scale. A single IT admin can run the scanner and scoring script part-time, provided each infrastructure owner (Windows, Linux, network) takes remediation ownership for their own systems rather than routing everything through one person. The automation in Steps 5-7 is what makes this feasible without a dedicated headcount.

What should I do about vulnerabilities with no available patch?

Apply a compensating control: network segmentation, a WAF rule blocking the known attack pattern, or disabling the vulnerable feature if it's not business-critical. Document the decision as a formal risk acceptance with a review date, so it doesn't quietly become permanent.

How do I handle vulnerabilities in third-party or vendor-managed systems?

Track them in the same inventory and pipeline, but the remediation owner becomes the vendor rather than an internal team. Set an SLA for vendor response and escalation, and treat a vendor missing that SLA on a KEV-listed CVE as grounds to apply your own compensating control in the meantime.

Related Coverage

Marcus Chen

Marcus Chen

Gaming & Consumer Tech Editor

Marcus Chen is a senior editor at Tech Insider, where he leads coverage of the US online gaming market, including sweepstakes and social casinos, alongside consumer technology. He evaluates operators on their published terms, licensing and RNG certifications, stated redemption policies, and corroborating independent reporting, and writes plainly about what the evidence supports. Tech Insider does not run first-party money tests and does not gamble with reader funds. Marcus has reported on the technology and online-gaming industries for more than a decade.

View all articles