
fleet-cve-scanner v1.1.0
Ein quelloffener, einzelskriptbasierter CVE-Scanner für RMM-verwaltete Flotten. Reines PowerShell 7 – kombiniert Ihre RMM-Softwareinventur mit NVD, CISA KEV, EPSS und SSVC, um die Frage zu beantworten: Ist diese Version anfällig und wie dringend ist das Problem?
fleet-cve-scanner
Ein Open-Source, Ein-Skript-CVE-Scanner für RMM-verwaltete Flotten. Reines PowerShell 7, keine Agents, keine Appliances, keine Lizenzgebühren.
Es beantwortet eine Frage für jede Software auf jedem von Ihnen verwalteten Endpunkt: ist die installierte Version bekanntermaßen verwundbar und wie dringend ist es? — indem es das Software-Inventar Ihres RMM mit kostenlosen, autoritativen Sicherheitsfeeds abgleicht:
- NIST NVD — bekannte CVEs + betroffene Versionsbereiche
- CISA KEV — aktiv in freier Wildbahn ausgenutzt (umgeht den Score-Schwellenwert)
- EPSS — Wahrscheinlichkeit der Ausnutzung in den nächsten 30 Tagen
- CISA SSVC — Act / Attend / Track-Priorisierungsentscheidungen
- MSRC — Nachweis, dass ein Microsoft-Patch den Fund bereits abdeckt
- endoflife.date — Software, die nie wieder gepatcht wird
Ausgaben: ein CSV-Bericht pro Gerät, eine SQLite-Historie mit SLA-Uhren und Woche-über-Woche-Änderungsereignissen, ein in sich geschlossenes HTML-Dashboard und ein optionaler Markdown-Expositionsbericht.
Status
v1.0.0. Die Engine wurde sauber aus einem Scanner portiert, der wöchentlich in Produktion gegen eine Windows-Flotte lief (siehe docs/HISTORY.md). Vor dieser Veröffentlichung wurden beide gegen dieselbe Live-Flotte ausgeführt und zeilenweise verglichen: über Zehntausende von Funden, bei denen beide eine identische (device, software, version) sahen, stimmte jedes berechnete Feld — Status, CVSS, Schweregrad, KEV-Flag, SSVC-Entscheidung, Fix-Version — exakt überein.
Dieser Vergleich deckt die Urteilslogik ab. Er hat die Historie/SLA-Ebene nicht getestet und ersetzt nicht die Validierung des Tools in Ihrer eigenen Umgebung. Lesen Sie docs/known-limitations.md, bevor Sie ihm vertrauen — es ist ehrlich geschrieben, nicht schmeichelhaft.
Schnellstart
Sie benötigen PowerShell 7 (pwsh). Zwei Möglichkeiten zur Ausführung:
A. Live-NinjaOne-Scan
cp config.example.json config.json
# edit config.json: fill ninjaone.client_id / client_secret / base_url,
# set output.report_dir, and (recommended) nvd.api_key
pwsh -File fleet-cve-scan.ps1
B. Offline — beliebiges RMM, SCCM oder Intune (kein API-Zugriff)
Exportieren Sie Ihr Inventar in eine CSV-Datei mit den Spalten hostname, software, version (optional device_id, os), dann:
cp config.example.json config.json # only output.report_dir is needed here
pwsh -File fleet-cve-scan.ps1 -InputCsv inventory.csv
-InputCsv führt null NinjaOne-Aufrufe durch — kein OAuth, keine API. Es fragt weiterhin NVD und die anderen öffentlichen Feeds ab, daher benötigen Sie weiterhin einen nvd.api_key in config.json und ein gültiges output.report_dir. Siehe docs/rmm-adapters.md für den Eingabevertrag und RMM-spezifische Exportrezepte.
Voraussetzungen installieren
- PowerShell 7 — macOS:
brew install powershell; Windows:winget install Microsoft.PowerShell; Debian/Ubuntu: aus dem Microsoft-Paket-Repo installieren (packages.microsoft.com). - sqlite3 (optional, ermöglicht Historie/Trends) — macOS: vorinstalliert oder
brew install sqlite; Debian/Ubuntu:apt-get install sqlite3; Windows: das SQLite „Tools“-Bundle von sqlite.org herunterladen undsqlite3.exein denPATHoder neben das Skript legen. Ohne es läuft der Scan trotzdem;first_seenfällt auf das Ausführungsdatum zurück.
Anforderungen
- PowerShell 7 (7.4+ empfohlen). Der parallele Scan erfordert 7.0+.
- sqlite3 — optional; ermöglicht die Historie-Datenbank, Trends und SLA
first_seen-Verfolgung. Fehlt es = diese Funktionen werden übersprungen, der Scan wird trotzdem abgeschlossen. - Netzwerk-Ausgang zu NVD, CISA (KEV),
epss.empiricalsecurity.com(dem EPSS-Bulk-Score-Host — FIRST hat das EPSS-Daten-Hosting dorthin verlegt), MSRC, endoflife.date und GitHub raw (CVE-ProgrammcvelistV5) — plus die NinjaOne API im Live-Modus. - Anmeldeinformationen — Live-Modus benötigt NinjaOne OAuth2-Client-ID/Secret; Offline-Modus benötigt nur ein Inventar-CSV. Ein NVD-API-Schlüssel ist technisch optional, aber im Flottenmaßstab praktisch erforderlich: NVD erlaubt 50 Anfragen/30s mit einem Schlüssel und nur 5/30s ohne. Der Scanner erkennt, welchen Sie haben, und drosselt entsprechend, sodass eine schlüssellose Ausführung korrekt, aber etwa 10x langsamer ist — in Ordnung für eine Handvoll Produkte, unpraktisch für eine echte Flotte. Schlüssel sind kostenlos.
Konfigurationsreferenz
Die Konfiguration ist eine JSON-Datei (standardmäßig config.json neben dem Skript; Überschreibung mit -ConfigPath). Beginnen Sie mit config.example.json. Jeder Schlüssel, den der Scanner liest, sein Standardwert, wenn er weggelassen wird, und was er tut:
ninjaone (Live-Modus)
| Key | Standard | Zweck |
|---|---|---|
ninjaone.client_id | "" | NinjaOne API OAuth2-Client-ID. Erforderlich für Live-Scans; für -InputCsv leer lassen. |
ninjaone.client_secret | "" | NinjaOne API OAuth2-Client-Geheimnis. |
ninjaone.base_url | — (Beispiel liefert https://app.ninjarmm.com) | NinjaOne API Basis-URL; erforderlich für Live-Scans, kein eingebauter Fallback. Verwenden Sie den Host Ihrer Region (z.B. eu.ninjarmm.com, oc.ninjarmm.com). |
nvd
| Key | Standard | Zweck |
|---|---|---|
nvd.api_key | "" | NVD-API-Schlüssel, als apiKey-Header gesendet. Leer läuft unauthentifiziert, was die Standardwerte für nvd_rate_limit und nvd_min_spacing_ms auf die anonyme Obergrenze von NVD (4/30s, 6500ms) anstatt auf die authentifizierte (48/30s, 700ms) setzt. Kostenlos und ~10x schneller. |
Scan & Bewertung
| Key | Standard | Zweck |
|---|---|---|
cvss_threshold | 7.0 | Minimaler CVSS-Basiswert, damit ein bewertetes CVE als VULNERABLE gemeldet wird. KEV-gelistete CVEs umgehen diese Untergrenze. |
ssvc_mission_prevalence | high | SSVC Mission Prevalence Stakeholder-Eingabe (low / medium / high). |
ssvc_public_wellbeing | high | SSVC Public Well-being Stakeholder-Eingabe (low / medium / high). |
Caching & Ratenbegrenzung
| Key | Standard | Zweck |
|---|---|---|
nvd_cache_ttl_days | 7 | Tage, die ein NVD-Ergebnis (pro Softwarename) zwischengespeichert bleibt, bevor es erneut abgefragt wird. |
nvd_cache_flush_every | 500 | NVD-Cache nach jeweils N abgeschlossenen Elementen auf die Festplatte sichern, damit ein abgebrochener Scan seine Abrufe behält. 0 deaktiviert (Ende-des-Scans-Speicherung läuft trotzdem). |
nvd_min_spacing_ms | 700 mit API-Schlüssel, 6500 ohne | Mindestmillisekunden zwischen NVD-Aufrufen. Die bindende Ratenbeschränkung — zuerst anpassen. |
nvd_rate_limit | 48 mit API-Schlüssel, 4 ohne | Gleitfenster-Obergrenze für NVD-Aufrufe pro 30s (Absicherung). Mindestens 1. |
parallel_throttle | 20 | ForEach-Object -Parallel Runspace-Anzahl. NVD-Aufrufe werden trotzdem serialisiert; dies begrenzt nur die Cache-Treffer-Parallelität. Mindestens 1. |
kev_cache_ttl_hours | 24 | TTL für den zwischengespeicherten CISA KEV-Katalog. |
epss_cache_ttl_hours | 24 | TTL für die zwischengespeicherte FIRST.org EPSS-Score-Datei. |
eol_cache_ttl_days | 7 | TTL für zwischengespeicherte endoflife.date-Abfragen. |
msrc_cache_ttl_days | 30 | TTL für zwischengespeicherte MSRC CVRF-Daten. |
cvelist_cache_ttl_days | 7 | TTL für zwischengespeicherte CVE-Programm (cvelistV5) / CISA-ADP SSVC-Daten. |
cvelist_fetch_budget | 300 | Maximale CVE-Datensatzabrufe pro Durchlauf für SSVC/Vulnrichment-Anreicherung; der Rest wird auf den nächsten Durchlauf verschoben. |
nvd_min_spacing_ms, nvd_rate_limit und parallel_throttle werden vom Scanner gelesen, sind aber nicht in config.example.json enthalten — fügen Sie sie nur hinzu, wenn Sie anpassen müssen. Siehe docs/rate-limiting.md.
Anreicherung & SLA
| Key | Standard | Zweck |
|---|---|---|
epss_spike_threshold | 0.10 | Mindestanstieg des EPSS-Scores eines Fundes zwischen Scans, um ein EPSS_SPIKE-Änderungsereignis auszulösen. |
sla_days_critical | 14 | SLA-Fenster (Tage ab first_seen) für KRITISCHE Funde. |
sla_days_high | 30 | SLA-Fenster für andere bewertete Schweregrade. |
denied_software_csv | "" | Pfad zu einem optionalen CSV-Abgleich für verbotene Software. Leer deaktiviert die Prüfung. |
output
| Key | Standard | Zweck |
|---|---|---|
output.report_dir | (erforderlich) | Verzeichnis für alle Ausgaben — CSV, Historie-DB, Dashboard und Caches. |
output.wiki_security_page | "" | Optionaler Pfad zu einer Markdown-Expositionsseite. Nur geschrieben, wenn gesetzt. |
output.dashboard_share | "" | Optionaler Pfad/Freigabe, der bei jedem Durchlauf eine Kopie des Dashboards erhält. Leer = nur lokal. |
Ausgaben
Alles unten landet in output.report_dir.
vuln-report-YYYY-MM-DD.csv— der Bericht pro Fund. 28 Spalten (Schema unten).vuln-history.db— SQLite-Historie (erfordertsqlite3):runs(Aggregate pro Durchlauf),findings(pro(device, software, cve)Historie offener Funde mitfirst_seen/last_seen/resolved_date/epss_score) undchanges(getippte Woche-über-Woche-Ereignisse).cve-dashboard.html— in sich geschlossenes HTML-Dashboard (Diagrammbibliothek eingebettet, keine externen Anfragen). Optional nachoutput.dashboard_sharekopiert.eol-report-YYYY-MM-DD.csv— geschrieben, wenn Familien am Ende ihrer Lebensdauer erkannt werden.denied-report-YYYY-MM-DD.csv— geschrieben, wenndenied_software_csvkonfiguriert ist und zutrifft.- Cache-Dateien —
nvd-cache.json,kev-cache.json,epss-cache.json,eol-cache.json,msrc-cache.json,cvelist-cache.json(alle gitignoriert). - Markdown-Expositionsseite — unter
output.wiki_security_page, nur wenn gesetzt.
CSV-Schema (28 Spalten)
Der Spaltensatz ist fest und durch eine Gate-Prüfung erzwungen. In Reihenfolge:
| # | Spalte | Bedeutung |
|---|---|---|
| 1 | device_id | RMM-Geräte-ID (oder eine deterministische synthetische ID im -InputCsv-Modus). |
| 2 | hostname | Geräte-Hostname. |
| 3 | os | Betriebssystemzeichenfolge (kann leer sein). |
| 4 | software | Produktname wie inventarisiert. |
| 5 | installed_version | Versionszeichenfolge wie inventarisiert. |
| 6 | cve_id | CVE-Bezeichner. |
| 7 | cvss_score | CVSS-Basiswert (0 wenn keiner). |
| 8 | severity | CVSS-Schweregradbezeichnung. |
| 9 | cvss_version | Welcher CVSS-Standard den Wert erzeugt hat (4.0/3.1/3.0/2.0/leer). |
| 10 | epss_score | FIRST.org EPSS-Wahrscheinlichkeit (leer, wenn das CVE nicht im Katalog ist). |
| 11 | epss_percentile | EPSS-Perzentil (leer, wenn nicht vorhanden). |
| 12 | ssvc_exploitation | Rohe CISA SSVC-Ausnutzungsoption (none/poc/active; leer ohne ADP-Daten). |
| 13 | ssvc_decision | SSVC-Entscheidung Act/Attend/Track (leer ohne ADP-Daten). |
| 14 | status | VULNERABLE / PATCHED / UNKNOWN. |
| 15 | published_date | CVE-Veröffentlichungsdatum. |
| 16 | unknown_reason | Warum eine Zeile UNKNOWN ist (SUSPECT_VERSION / NO_VERSION_BOUND / VERSION_PARSE_ERROR; sonst leer). |
| 17 | is_kev | True, wenn das CVE im CISA KEV-Katalog ist. |
| 18 | kev_due_date | CISA KEV-Frist für Behebung. |
| 19 | kev_ransomware_use | KEV-Flag für Ransomware-Kampagnennutzung. |
| 20 | fix_version | Berechnete Fix-Grenzversion. |
| 21 | fix_boundary_inclusive | Ob die Fix-Grenze inklusiv ist. |
| 22 | advisory_url | Vendor-Advisory oder NVD-Detail-URL. |
| 23 | patch_evidence | Nachweis, wenn ein Microsoft-UNKNOWN auf PATCHED geändert wurde. |
| 24 | triage_status | Triage-Disposition aus triage.json (leer, wenn keine). |
| 25 | triage_note | Triage-Notiz. |
| 26 | first_seen | Datum, an dem der Fund erstmals beobachtet wurde (aus der Historie-DB). |
| 27 | sla_due | SLA-Fälligkeitsdatum. |
| 28 | sla_breach | Ob die SLA verletzt ist. |
Zeitplanung
Run-WeeklyScan.ps1 ist ein Wrapper für geplante Ausführungen: Es führt den Scanner aus seinem eigenen Verzeichnis aus, erfasst alle Ausgabeströme in einem datierten Log unter <script dir>/logs/ (überschreibbar mit -LogDir) und gibt den Exit-Code des Scanners weiter, sodass der Scheduler Fehler sieht.
Windows Task Scheduler:
schtasks /Create /TN "CVE-Scan-Weekly" ^
/TR "pwsh -NoProfile -File C:\path\to\Run-WeeklyScan.ps1" ^
/SC WEEKLY /D SUN /ST 22:00 /RU SYSTEM
cron (Linux/macOS):
0 22 * * 0 pwsh -File /path/to/Run-WeeklyScan.ps1
Dashboard-Hosting
cve-dashboard.html ist vollständig in sich geschlossen — öffnen Sie es direkt, legen Sie es auf einem beliebigen statischen Host oder einer Dateifreigabe ab, oder kopieren Sie es automatisch bei jedem Durchlauf über output.dashboard_share. Für zugriffsgeschütztes Hosting enthält das dashboard-auth/-Verzeichnis einen minimalen ASP.NET-Host mit Microsoft Entra (Azure AD)-Authentifizierung; siehe dessen README und RUNBOOK.
Ehrliche Einschränkungen (lesen Sie es, bevor Sie ihm vertrauen)
- CPE-Abgleich ist von Natur aus unscharf. 40–60% des typischen Inventars hat überhaupt keinen NVD-Eintrag und wird übersprungen (protokolliert, nie stillschweigend). Die namensbasierte CPE-Auswahl kann das falsche Produkt wählen; ein Unterdrückungsworkflow existiert, weil er notwendig ist. Dies ist die Obergrenze des freien NVD-Ansatzes — ein bezahlter Scanner mit einem kuratierten Erkennungskatalog wird genauer sein. Der Wert dieses Tools liegt darin, kostenlos, transparent und überprüfbar zu sein.
- UNKNOWN ist ein erstklassiges Urteil. Wenn der Scanner nicht beweisen kann, verwundbar oder gepatcht zu sein, sagt er das, anstatt zu raten.
- Nur gegen NinjaOne verifiziert. Die Scan-Engine ist RMM-agnostisch (Eingabe ist nur Gerät + Software + Version); der
-InputCsv-Modus erlaubt jedem RMM/SCCM/Intune-Export, sie zu füttern, aber nur der NinjaOne-Adapter wurde in Produktion eingesetzt.
Die vollständige, ungeschönte Liste — die CPE-Überspringungsrate, Dedup-Key-Masking, Seite-1-totalResults, MSRC-Supersedence-Lücken und mehr — finden Sie in docs/known-limitations.md.
Designhinweise
Einige strukturelle Entscheidungen, die unkonventionell aussehen, sind bewusst:
- Einzelne Datei, kein Modul. Der heiße Scan-Pfad läuft innerhalb von
ForEach-Object -Parallel, und diese Runspaces können keine Funktionen im äußeren Gültigkeitsbereich sehen — weshalb einige Hilfsfunktionen absichtlich innerhalb des parallelen Blocks dupliziert sind. Ein Aufbau mit einer Funktion pro Datei würde diese Duplizierung nicht beseitigen (der parallele Körper müsste den Quellcode trotzdem pro Runspace injiziert bekommen); es würde nur einen Build-Schritt hinzufügen und das Ein-Datei-Bereitstellungsmodell brechen. Falls das Projekt jemals über eine einzelne Datei hinauswächst, ist die Paketierung die v2-förmige Änderung, kein Patch. [PSCustomObject]-Datensätze, keine Klassen. Funddatensätze werden innerhalb paralleler Runspaces erstellt und zurück zum Haupt-Runspace gemarshallt. PowerShell-Klasseninstanzen überleben diese Grenze nicht zuverlässig;[PSCustomObject]schon, und der CSV-Spaltenvertrag wird durch das Release-Gate anstelle eines Typsystems durchgesetzt.- Einfache PS7-Testskripte, nicht Pester. Die Testreihen in
tests/sind abhängigkeitsfreie Skripte mit Exit-Code-Überprüfungen, die überall dort ausgeführt werden können, wopwshexistiert — einschließlich des Scan-Servers — ohne etwas zu installieren. CI führt sie alle auf Windows und Ubuntu aus.
Dokumentation
- docs/how-it-works.md — die Scan-Pipeline Schritt für Schritt, Urteilslogik, Cache-Disziplin und der Fail-Soft-Vertrag.
- docs/known-limitations.md — jede bekannte Art, wie der Scanner falsch liegen kann, und warum.
- docs/rate-limiting.md — die NVD-Ratenbegrenzungslektionen als Bedienerleitfaden und Abstimmungstabelle.
- docs/rmm-adapters.md — der Eingabevertrag, die beiden eingebauten Inventarpfade und wie man einen nativen Adapter hinzufügt.
- docs/HISTORY.md — komprimierte, bereinigte Versionshistorie und die technischen Lektionen hinter jeder Veröffentlichung.
Mitwirken
PRs willkommen — vor allem RMM-Adapter. Siehe CONTRIBUTING.md für die Grundregeln und die Anforderung der sicheren Richtung (Statuslogik darf nur in Richtung mehr Sichtbarkeit irren, niemals stillschweigend in Richtung PATCHED).
Datenquellen & Quellenangabe
Dieses Tool ist nur so nützlich wie die kostenlosen, autoritativen Feeds, gegen die es abgleicht. Jeder der folgenden wird zur Scan-Zeit abgefragt; bitte respektieren Sie deren Bedingungen, wenn Sie darauf aufbauen.
- NIST National Vulnerability Database (NVD) — CVE-Datensätze und betroffene Versionsbereiche über die NVD-API. Dieses Produkt verwendet die NVD-API, wird aber nicht von der NVD unterstützt oder zertifiziert. https://nvd.nist.gov/
- CISA Known Exploited Vulnerabilities (KEV) Catalog — die maßgebliche Liste von CVEs, die bekanntermaßen in freier Wildbahn ausgenutzt wurden. https://www.cisa.gov/known-exploited-vulnerabilities-catalog
- CVE Program —
cvelistV5— CVE-Datensätze plus die SSVC/Vulnrichment-Daten, die vom CISA ADP beigesteuert wurden. CVE® ist eine eingetragene Marke der MITRE Corporation. https://github.com/CVEProject/cvelistV5 - SSVC (Stakeholder-Specific Vulnerability Categorization) — die Act/Attend/Track-Entscheidungslogik in diesem Tool basiert auf CERT/CC SSVC (Carnegie Mellon University Software Engineering Institute) und dem CISA SSVC Coordinator-Entscheidungsbaum, Version 2.0.3. Eine Anerkennung von CERT/CC und CISA ist erforderlich. https://www.cisa.gov/ssvc · https://github.com/CERTCC/SSVC
- FIRST.org EPSS (Exploit Prediction Scoring System) — Wahrscheinlichkeit der Ausnutzung in den nächsten 30 Tagen. EPSS-Daten werden von FIRST bereitgestellt. Siehe Jay Jacobs, Sasha Romanosky, Benjamin Edwards, Michael Roytman, Idris Adjerid (2021), Exploit Prediction Scoring System, Digital Threats: Research and Practice, 2(3). https://www.first.org/epss/
- Microsoft Security Response Center (MSRC) — die CVRF-API, die verwendet wird, um zu bestätigen, ob ein Microsoft-Sicherheitsupdate einen Fund bereits abdeckt. © Microsoft Corporation. https://api.msrc.microsoft.com/ · https://msrc.microsoft.com/
- endoflife.date — End-of-Life- und End-of-Support-Daten für Software, die nie wieder gepatcht wird. Daten mit freundlicher Genehmigung des endoflife.date-Projekts. https://endoflife.date/
Lizenz
Apache-2.0 — siehe LICENSE.