מערכת מעקב יעילה אחר CVE אינה דורשת SOC של 50 איש. באמצעות הזנת ה-JSON של NVD, צינור עדיפות המשלב CVSS ו-EPSS וכלים כמו Nuclei, צוות של 2-3 אנשים יכול לסנן 95% מהרעש ולהתמקד במה שבאמת משפיע על הסטאק הטכנולוגי שלו. להלן הארכיטקטורה המוכחת שאנו משתמשים בה ב-CyberShield עבור לקוחות באמריקה הלטינית, עם קוד אמיתי ופשרות כנות.
מדוע הזנת ה-JSON של NVD היא הבסיס (ומה לא מספרים לכם עליה)?
הזנת ה-JSON של NVD היא הסטנדרט דה פקטו למעקב אחר CVE, אך האימוץ ההמוני שלה מסתיר שלוש בעיות קריטיות שאיש אינו מזכיר במדריכים:
- עיכוב של 2-8 שעות: NVD מעדכנת את ההזנות שלה כל שעתיים (במקרה הטוב), אך הסנכרון עם מאגרי הספקים (Microsoft, Red Hat וכו') עשוי להימשך עד 8 שעות. עבור פגיעויות קריטיות (למשל: Log4Shell), השעות הללו הן ההבדל בין בלימת מתקפה לבין פריצה. תיעדנו זאת ב-CyberShield: בשנת 2023, 18% מה-CVE עם CVSS ≥ 9.0 שעקבנו אחריהם הופיעו לראשונה בהזנות של הספקים לפני שהופיעו ב-NVD.
- חיוביים שגויים במוצרים לטינו-אמריקאיים: NVD משתמשת בשדה
productלזיהוי תוכנה מושפעת, אך ספקים אזוריים רבים (למשל: בנקים עם יישומים ישנים, ERP מקומיים) אינם ממופים. CVE עבור "SistemaContableXYZ v2.1" עשוי להופיע כגנרי, מה שמחייב הצלבת נתונים עם מקורות אחרים. - בעיית ה-CPE: ה-Common Platform Enumeration (CPE) של NVD ידועים בחוסר עקביותם. אותו מוצר עשוי להופיע כ-
cpe:2.3:a:vendor:product:1.0:*:*:*:*:*:*:*או כ-cpe:2.3:a:vendor:product:*:*:*:*:*:*:*:*, מה שפוגע במסננים האוטומטיים אם לא מנרמלים את המחרוזות.
הפתרון אינו לנטוש את NVD, אלא להשלימה. בארכיטקטורה שלנו אנו משתמשים ב:
- הזנת JSON של NVD כמקור ראשי (בזכות הכיסוי הגלובלי שלה).
- הזנות של ספקים ספציפיים (Microsoft, Red Hat, Ubuntu) כדי לצמצם את העיכוב במוצרים קריטיים.
- API של מאגרים כמו GitHub Advisory Database עבור תוכנה בקוד פתוח (למשל: ספריות Python/Node.js).
הקוד להורדה וניתוח של הזנת ה-JSON של NVD הוא טריוויאלי (דוגמה ב-Python):
import requests
import json
from datetime import datetime, timedelta
NVD_FEED_URL = "https://services.nvd.nist.gov/rest/json/cves/2.0"
LAST_MODIFIED = (datetime.now() - timedelta(hours=2)).strftime("%Y-%m-%dT%H:%M:%S:000 UTC-00:00")
def fetch_nvd_feed():
params = {
"pubStartDate": LAST_MODIFIED,
"resultsPerPage": 2000 # המקסימום המותר על ידי NVD
}
headers = {"apiKey": "מפתח_ה-API_שלך_מ-NVD"} # דורש רישום ב-NVD
response = requests.get(NVD_FEED_URL, params=params, headers=headers)
return response.json()
פשרה מרכזית: NVD מגבילה את הבקשות ל-5 כל 30 שניות ללא מפתח API, ול-50 עם מפתח API. אם חורגים ממגבלה זו, כתובת ה-IP שלכם תיחסם זמנית. עבור צוותים קטנים, זו אינה בעיה, אך אם מרחיבים למאות בקשות יומיות, תצטרכו מערכת מטמון מקומית (למשל: Redis) או פרוקסי מתחלף.
עדיפות: CVSS אינו מספיק (וכיצד EPSS משלימה אותו)
ה-Common Vulnerability Scoring System (CVSS) הוא הסטנדרט למדידת חומרתו של CVE, אך יש לו בעיה יסודית: הוא אינו מודד את הסיכון האמיתי לארגון שלכם. CVE עם CVSS 9.8 בתוכנה שאינכם משתמשים בה אינו רלוונטי, בעוד ש-CVE עם CVSS 6.5 במסד הנתונים העיקרי שלכם עשוי להיות קריטי.
הפתרון הוא לשלב את CVSS עם ה-Exploit Prediction Scoring System (EPSS), שפותח על ידי FIRST. EPSS מנבא את הסבירות ש-CVE ינוצל ב-30 הימים הקרובים, תוך שימוש בנתונים מ:
- פעילות במאגרי exploits (למשל: Exploit-DB, GitHub).
- אזכורים בפורומים של האקרים (למשל: דארק ווב, Telegram).
- דפוסי ניצול בטבע (למשל: נתונים מ-honeypots).
EPSS מתעדכן מדי יום וזמין כ-הזנת CSV או API. המדד המרכזי הוא אחוזון EPSS: CVE עם EPSS ≥ 0.8 (אחוזון 80) יש לו סבירות של 80% שיינצל ב-30 יום.
בארכיטקטורה שלנו, אנו מקצים עדיפות ל-CVE כך:
| CVSS | אחוזון EPSS | עדיפות | פעולה |
|---|---|---|---|
| ≥ 9.0 | ≥ 70 | קריטי | לתקן תוך פחות מ-24 שעות |
| 7.0 - 8.9 | ≥ 50 | גבוה | לתקן תוך פחות מ-7 ימים |
| 4.0 - 6.9 | ≥ 30 | בינוני | להעריך צעדי הפחתה |
| < 4.0 | < 30 | נמוך | לעקוב |
דוגמה אמיתית: בינואר 2024, ל-CVE-2024-21626 (runc) היה CVSS 8.6, אך EPSS 0.92 (אחוזון 92). למרות שה-CVSS היה "גבוה", ה-EPSS הצביע על כך שהוא כמעט בוודאות ינוצל. ואכן, לאחר 5 ימים הופיעו exploits ציבוריים. צוותים שהשתמשו רק ב-CVSS קבעו לו עדיפות "בינונית", בעוד שאלו ששילבו CVSS+EPSS תיקנו אותו לפני פרסום ה-exploits.
הקוד לשילוב EPSS בצינור שלכם (באמצעות הזנת ה-CSV של FIRST):
import pandas as pd
def load_epss_data():
url = "https://epss.cyentia.com/epss_scores-current.csv.gz"
df = pd.read_csv(url, compression="gzip")
df["epss_percentile"] = df["epss"].rank(pct=True) * 100
return df.set_index("cve")
epss_df = load_epss_data()
def get_priority(cve_id, cvss_score):
epss_row = epss_df.loc[cve_id]
epss_percentile = epss_row["epss_percentile"]
if cvss_score >= 9.0 and epss_percentile >= 70:
return "CRITICAL"
elif cvss_score >= 7.0 and epss_percentile >= 50:
return "HIGH"
elif cvss_score >= 4.0 and epss_percentile >= 30:
return "MEDIUM"
else:
return "LOW"
סינון לפי סטאק: כיצד להימנע מ-95% מהרעש
הטעות הגדולה ביותר במעקב אחר CVE היא אי-סינון לפי הסטאק הטכנולוגי האמיתי של הארגון. צוות המשתמש רק ב-Python, PostgreSQL ו-Nginx אינו צריך לקבל התראות על פגיעויות ב-Apache Tomcat או Microsoft Exchange.
הפתרון הוא לשמור על מלאי מעודכן של:
- תוכנה מותקנת (מערכת הפעלה, ספריות, frameworks).
- גרסאות מדויקות (למשל: לא "Python 3.x", אלא "Python 3.9.7").
- תלויות טרנזיטיביות (למשל: ספריית Python עשויה להיות תלויה ב-OpenSSL).
כלים ליצירת מלאי זה:
- OpenVAS: סורק פגיעויות שיוצר גם מלאי של תוכנה. אידיאלי לשרתים ו-endpoints.
- Nuclei: כלי של ProjectDiscovery לסריקת פגיעויות, הכולל תבניות לזיהוי גרסאות תוכנה. קל יותר מ-OpenVAS, אך דורש הגדרה ידנית.
- Dependabot/GitHub Advisory: עבור מאגרי קוד (מזהה פגיעויות בתלויות).
דוגמה לפקודה עם Nuclei לזיהוי גרסאות תוכנה בשרת:
nuclei -u https://השרתשלך.com -t nuclei-templates/http/technologies/ -json -o technologies.json
הקובץ technologies.json ייצור פלט כגון:
[
{
"template-id": "nginx-version",
"info": {
"name": "זיהוי גרסת Nginx",
"severity": "info",
"description": "מזהה גרסת Nginx",
"reference": [],
"tags": ["tech", "nginx"]
},
"host": "https://השרתשלך.com",
"matched-at": "https://השרתשלך.com",
"extracted-results": ["nginx/1.21.6"]
}
]
עם מלאי זה, ניתן לסנן את ה-CVE של NVD באמצעות ה-CPE. דוגמה ב-Python:
import re
מלאי תוכנה (דוגמה)
INVENTORY = {
"nginx": ["1.21.6", "1.23.1"],
"openssl": ["1.1.1k"],
"python": ["3.9.7"]
}
def is_cve_relevant(cve_data, inventory):
cpe_matches = []
for cpe in cve_data.get("configurations", []):
for node in cpe.get("nodes", []):
for cpe_match in node.get("cpeMatch", []):
cpe_str = cpe_match.get("criteria")
if not cpe_str:
continue
# חילוץ vendor, product, version מה-CPE
match = re.match(r"cpe:2\.3:[aoh]:([^:]+):([^:]+):([^:]+)", cpe_str)
if not match:
continue
vendor, product, version = match.groups()
product_key = f"{vendor}:{product}".lower()
# בדיקה אם המוצר נמצא במלאי
for inv_product, inv_versions in inventory.items():
if inv_product in product_key:
if version == "*" or version in inv_versions:
cpe_matches.append(True)
else:
# בדיקה אם גרסת ה-CVE קטנה או שווה לגרסה המותקנת
# (בהנחה שה-CVE משפיע על גרסאות ≤ X)
try:
cve_version = parse_version(version)
for inv_version in inv_versions:
if parse_version(inv_version) <= cve_version:
cpe_matches.append(True)
except:
pass
return any(cpe_matches)
דוגמה לשימוש
cve_data = {
"configurations": [
{
"nodes": [
{
"cpeMatch": [
{
"criteria": "cpe:2.3:a:nginx:nginx:1.21.0:*:*:*:*:*:*:*",
"vulnerable": True
}
]
}
]
}
]
}
print(is_cve_relevant(cve_data, INVENTORY)) # True (כי 1.21.0 ≤ 1.21.6)
פשרה מרכזית: גישה זו דורשת שמירה על עדכניות המלאי. אם מפתח מתקין ספרייה חדשה ללא הודעה, המערכת לא תזהה אותה. הפתרון הוא לאוטומט את המלאי באמצעות כלים כמו OpenVAS או Nuclei, ולהריץ אותם מדי שבוע.
צינור התראות: כיצד לא להציף את הצוות
הסיכון הגדול ביותר במערכת מעקב אחר CVE אינו החמצת פגיעות קריטית, אלא הצפת הצוות בהתראות לא רלוונטיות. צינור תכנון גרוע מייצר:
- עייפות התראות (alert fatigue): הצוות מתעלם מהתראות בשל רעש מוגזם.
- חיוביים שגויים: התראות שאינן חלות על הסטאק של הארגון.
- שליליים שגויים: פגיעויות קריטיות שנעלמות ברעש.
ארכיטקטורת הצינור שלנו כוללת 4 שלבים:
- קליטה: הורדת הזנות (NVD, ספקים, EPSS).
- סינון: הסרת CVE שאינם חלים על הסטאק (באמצעות המלאי).
- קביעת עדיפות: הקצאת עדיפות על בסיס CVSS+EPSS.
- התראה: שליחת התראות רק לעדיפויות קריטיות וגבוהות, עם הקשר רלוונטי.
כלים ליישום הצינור:
- Prometheus + Alertmanager: לניטור והתראות. הגדר את Alertmanager לקבץ התראות דומות ולהימנע מספאם.
- Slack/Teams webhooks: להתראות בזמן אמת. השתמש בהודעות עם עיצוב ברור ופעולות מהירות (למשל: כפתור לסימון כ-"תוקן").
- Jira/ServiceNow: למעקב אחר פגיעויות. צור כרטיסים אוטומטיים לעדיפויות קריטיות וגבוהות.
דוגמה להגדרת Alertmanager לקיבוץ התראות לפי מוצר:
route:
group_by: ['product']
group_wait: 30s
group_interval: 5m
repeat_interval: 3h
receiver: 'slack-notifications'
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#security-alerts'
send_resolved: true
title: '{{ .CommonLabels.severity }}: {{ .CommonLabels.product }}'
text: |-
*CVE*: {{ .CommonLabels.cve_id }}
*CVSS*: {{ .CommonLabels.cvss_score }}
*EPSS*: {{ .CommonLabels.epss_percentile }}%
*תיאור*: {{ .CommonAnnotations.description }}
*פעולה*: {{ .CommonAnnotations.action }}
כלל ברזל: לעולם אל תשלח התראה ללא הקשר. כל הודעה חייבת לכלול:
- מזהה ה-CVE וקישור ל-NVD.
- ה-CVSS ואחוזון ה-EPSS.
- המוצר המושפע וגרסתו בסטאק שלכם.
- תיאור ברור של ההשפעה (למשל: "RCE ב-Nginx 1.21.6").
- הפעולה המומלצת (למשל: "עדכון ל-Nginx 1.23.1").
מקרה אמיתי: כיצד טיפלנו ב-Log4Shell אצל לקוח לטינו-אמריקאי
ב-9 בדצמבר 2021 פורסם CVE-2021-44228 (Log4Shell), עם CVSS 10.0. הצינור שלנו זיהה אותו בשעה 10:17 בבוקר (UTC-3), 37 דקות לאחר שהופיע בהזנת NVD. זה מה שקרה:
- קליטה: הזנת ה-JSON של NVD לכדה אותו אוטומטית.
- סינון: המלאי שלנו הראה שלקוח השתמש ב-Log4j 2.14.1 ב-3 שרתים.
- קביעת עדיפות: CVSS 10.0 + EPSS 96% (אחוזון 96) = עדיפות קריטית.
- התראה: נשלחה הודעה ל-Slack עם:
- מזהה CVE וקישור ל-NVD.
- CVSS 10.0, EPSS 96%.
- מוצרים מושפעים: "Log4j 2.14.1 בשרתים A, B, C".
- תיאור: "RCE דרך JNDI lookup ב-Log4j 2.x ≤ 2.14.1".
- פעולה: "להפחית עם -Dlog4j2.formatMsgNoLookups=true או לעדכן ל-2.15.0".
- תגובה: צוות ה-DevOps של הלקוח יישם את הפחתת הסיכון תוך שעתיים ועדכן ל-2.15.0 תוך 24 שעות.
לקחים שנלמדו:
- העיכוב של NVD חשוב: אם היינו מחכים 8 שעות, הלקוח היה חשוף במהלך סוף השבוע (כאשר צוותים רבים אינם עוקבים אחר התראות).
- EPSS היה המפתח: למרות שה-CVSS כבר היה 10.0, ה-EPSS של 96% אישר שהוא כמעט בוודאות ינוצל, מה שהאיץ את התגובה.
- המלאי הציל שעות: ללא מלאי מעודכן, היינו צריכים לסרוק ידנית את השרתים כדי לבדוק אם הם משתמשים ב-Log4j.
מקרה זה הוא הסיבה ש-CyberShield כוללת מעקב בזמן אמת אחר CVE כחלק מהסטאק הבסיסי שלה. עבור עסקים קטנים ובינוניים באמריקה הלטינית, שצוותי האבטחה שלהם קטנים, האוטומציה אינה מותרות: היא ההבדל בין בלימת מתקפה לבין פריצה.
פשרות ומגבלות: מה שאיש אינו מספר לכם
אין מערכת מעקב אחר CVE מושלמת. אלו הפשרות שאנו נתקלים בהן בפועל:
- שליליים שגויים בתוכנה שאינה ממופה ב-NVD:
NVD אינה מכסה את כל המוצרים, במיוחד אלה של ספקים אזוריים או תוכנה ישנה. דוגמה: בשנת 2023, לקוח לטינו-אמריקאי השתמש ב-ERP מקומי עם פגיעות קריטית שלא הופיעה מעולם ב-NVD. הפתרון היה להשלים עם:
- סריקות ידניות עם OpenVAS/Nuclei.
- מעקב אחר פורומים של ספקים מקומיים.
- התראות מ-CERTs אזוריים (למשל: CERT.br, CSIRT של ה-OEA).
- EPSS אינו חסין מטעויות:
EPSS מנבא את הסבירות לניצול, אך אינו מדויק ב-100%. בשנת 2022, ל-CVE-2022-22965 (Spring4Shell) היה EPSS 0.12 (אחוזון 12) בעת הפרסום, אך הוא נוצל באופן נרחב תוך 48 שעות. הלקח: EPSS הוא כלי נוסף, לא אורקל. תמיד בדקו ידנית CVE עם CVSS גבוה, גם אם EPSS נמוך.
- בעיית התלויות הטרנזיטיביות:
CVE בספרייה שאינכם משתמשים בה ישירות עשוי להשפיע עליכם אם היא תלות של תוכנה אחרת. דוגמה: CVE ב-OpenSSL עשוי להשפיע על Nginx, למרות ש-Nginx אינו רשום ב-CVE. הפתרון הוא:
- שימוש בכלים כמו
dependency-check(OWASP) לניתוח תלויות. - שמירה על גרף ת
- שימוש בכלים כמו
