דילוג לתוכן
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

כתיבת חוק Sigma: הזיהוי הראשון שלך, כולל בדיקה

כותבים חוק Sigma לכשלי התחברות ב-Windows, מאמתים אותו, ממירים ל-Splunk ובודקים מול אירועים שאמורים להתאים ואירועים שלא.

ביניים11 דקות קריאהמאת NotHoly

מה זה חוק Sigma

Sigma הוא פורמט פתוח, שלא קשור לאף יצרן, לתיאור זיהוי. כותבים את הלוגיקה פעם אחת בקובץ YAML קטן, וממיר הופך אותה לשפת השאילתות של ה-SIEM או של פלטפורמת הלוגים שרצה אצלך בפועל. ל-Splunk, ל-Elastic, ל-Microsoft Sentinel ולבקאנדים נוספים יש ממירים, ואותו חוק יכול לכוון לכמה מהם.

את הפורמט מתחזק פרויקט SigmaHQ. המפרט נמצא במאגר sigma-specification, והתיעוד של החוקים ב-sigmahq.io מסביר את השדות אחד אחד. אם יודעים לקרוא אירוע של Windows, החלק הקשה כבר מוכר. חוק Sigma הוא רק דרך לומר "האירוע הזה, עם ערכי השדות האלה" בצורה שאנשים אחרים וכלים אחרים יכולים לקרוא.

למה זה חשוב

רוב לוגיקת הזיהוי נכתבת ישירות בשפת השאילתות של מוצר אחד. חיפוש של Splunk לא רץ ב-Sentinel, ושאילתת KQL לא רצה ב-Splunk. כשצוות עובר פלטפורמה, או כשקהילה משתפת זיהוי, צריך לכתוב את הלוגיקה מחדש מאפס. זה איטי, והשכתוב מכניס טעויות.

Sigma מפריד בין לוגיקת הזיהוי לשפת השאילתות. החוק מתאר את מקור הלוגים ואת התנאי, והבקאנד הופך את זה לשאילתה. יש לזה שתי תוצאות:

  • אפשר לשתף חוק, לעבור עליו ולנהל לו גרסאות כטקסט, בדיוק כמו ב-code review.
  • אפשר לבדוק את אותו חוק מול אירועים לדוגמה לפני שהוא מגיע ל-SIEM בפרודקשן.

המאמר הזה עוסק בנקודה השנייה. חוק Sigma שעובר פרסור והמרה עדיין לא גמור: התחביר שלו תקין, וזה הכול. הוא נעשה שימושי כשאפשר להראות שהוא מתריע על האירועים הנכונים ושותק על האירועים שצריך להתעלם מהם.

החלקים של חוק

זה החוק שנבנה במעבדה, עם הסבר על כל שדה.

yaml
title: Failed Logon Event
id: bd37e49d-15ab-4e88-a3fb-53594e831b27
status: experimental
description: Detects Windows Security event 4625 (an account failed to log on).
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4625
    condition: selection
falsepositives:
    - Users mistyping passwords
level: low
tags:
    - attack.credential-access
    - attack.t1110

ה-UUID שלך יהיה שונה. צריך ליצור UUID משלך בכל ספריית UUID, למשל python -c "import uuid; print(uuid.uuid4())", ולשמור עליו גם כשעורכים את החוק בהמשך. לפי השדה id כלים אחרים עוקבים אחרי אותו חוק לאורך גרסאות.

  • title הוא שם קצר לבני אדם. הוא מופיע בהתראות וברשימות חוקים.
  • id הוא UUID שמזהה את החוק, ולא את התוכן שלו. הוא נשאר קבוע גם כשמשנים את הלוגיקה.
  • status אומר לקוראים עד כמה לסמוך על החוק. הערכים המותרים הם stable, test, experimental, deprecated, unsupported. חוק חדש שלא נבדק בסביבה אמיתית מקומו בסטטוס experimental.
  • description אומר במשפט או שניים מה החוק מחפש.
  • logsource אומר מאיפה האירועים מגיעים. הצירוף של product: windows עם service: security מצביע על לוג Security של Windows. שדות אחרים, כמו category, מאפשרים לתאר סוג של לוגים, למשל יצירת תהליכים, בלי לנקוב בערוץ המדויק.
  • detection מחזיק את לוגיקת ההתאמה. כל בלוק עם שם, כאן selection, הוא קבוצה של תנאים על שדות. שמות השדות הם אלה שמקור הלוגים משתמש בהם, והערכים מותאמים מול השדות האלה.
  • condition אומר איך הבלוקים מתחברים. הערך selection לבדו פירושו "אירועים שמתאימים לבלוק selection". בהמשך מוסיפים בלוקי סינון ומחברים אותם עם and ועם not.
  • falsepositives מפרט את המקרים התמימים שגם יתאימו. הרישום הזה הוא חלק מהחוק עצמו.
  • level היא החומרה שהחוק מקבל כשהוא מתריע. הערכים המותרים הם informational, low, medium, high, critical. בוחרים את הרמה לפי המשמעות של התראה בסביבה שלך, בלי קשר למידת הריגוש שהטכניקה מעוררת.
  • tags ממפה את החוק למסגרות עבודה. הקידומת attack. מצביעה על טקטיקה של MITRE ATT&CK, והתגית attack.t1110 מצביעה על הטכניקה T1110, Brute Force.

שתי הערות על החוק הזה. ראשית, EventID הוא שם השדה בעולם של Sigma, והוא מתייחס ל-Event ID שרואים ב-Event Viewer. שנית, level: low הוא החלטה. כשל התחברות בודד הוא דבר נפוץ. החוק בצורתו הנוכחית מתריע על כל 4625, ולכן רמה נמוכה היא נקודת הפתיחה הכנה עד שמוסיפים סף.

מיפוי Sigma לשפת שאילתות של SIEM

Sigma לא מכיר את ה-SIEM שלך. הבקאנד מכיר. הממיר קורא את הזיהוי, מפעיל processing pipeline שמשנה שמות שדות ומוסיף מגבלות של אינדקס או מקור, וכותב שאילתה בשפת היעד.

Processing pipeline הוא אוסף של כללי מיפוי. הוא עונה על שאלות כמו: מה שם השדה של Event ID ב-Splunk, באיזה אינדקס נמצאים אירועי Windows Security, ובאיזה sourcetype השאילתה צריכה לחפש. בלי pipeline, ממיר עלול לפלוט את שם השדה של Sigma כפי שהוא, ולעתים קרובות הפלט הזה לא שמיש בפלטפורמה שבשימוש.

לכל בקאנד יש pipelines משלו. חלקם חובה, והממיר מסרב להמשיך בלעדיהם אלא אם מבקשים ממנו במפורש לדלג על ה-pipeline. זה יופיע במעבדה.

הרעיון פשוט: החוק נשאר אותו חוק, וה-pipeline מתאים אותו לפלטפורמה. במעבר מ-Splunk ל-SIEM אחר מחליפים את הבקאנד ואת ה-pipeline, והזיהוי עצמו לא משתנה.

מעבדה מעשית: כתיבה, בדיקה והמרה של החוק

המעבדה רצה על המחשב שלך. לא צריך דומיין, domain controller או מערכת יעד כלשהי. העבודה כאן היא כתיבה והמרה של חוק. לא מייצרים כאן כשלי התחברות מול שום דבר, ואין לנסות לעשות זאת מול מערכת שאינה בבעלותך או שאין לך הרשאה בכתב לבדוק אותה.

המעבדה הורצה על Windows 11 עם Git Bash, עם Python 3.12 ועם לוג Windows Security בברירת המחדל. הפקודות זהות ב-Linux וב-macOS, חוץ מהנתיב לסביבה הווירטואלית.

שלב 1: יצירת סביבה וירטואלית והתקנת sigma-cli

יש ליצור תיקייה למעבדה, ואז להריץ:

bash
python3 -m venv .venv
. .venv/bin/activate
pip install sigma-cli

ב-Windows עם Git Bash, ייתכן שהפקודה python3 לא נמצאת ב-PATH. אפשר להשתמש בנתיב המלא להתקנה של Python 3.12, או בפקודה py -3.12. בנוסף, הסביבה הווירטואלית שמה את הסקריפטים שלה בתיקייה .venv/Scripts במקום .venv/bin, ולכן מפעילים אותה כך:

bash
. .venv/Scripts/activate

בדיקת ההתקנה:

bash
sigma version

במחשב שעליו נכתב המאמר הותקנה החבילה sigma-cli 4.0.0 עם pysigma 2.0.2 ו-Python 3.12.10. אצלך הגרסאות עשויות להיות חדשות יותר. את פלט הגרסה צריך לרשום בהערות שלך, ולא את המספרים שכאן.

שלב 2: כתיבת החוק

יש ליצור קובץ בשם failed_logon.yml עם התוכן מהסעיף הקודם, וליצור UUID משלך עבור השדה id. שומרים את הקובץ ב-UTF-8, בלי טאבים בהזחה של ה-YAML.

שלב 3: אימות החוק

bash
sigma check failed_logon.yml

הפקודה sigma check מפרסרת את הקובץ ומריצה ולידטורים. היא תופסת שדה חסר, תגית פגומה, סטטוס או רמה לא מוכרים, וכמה בעיות סגנון.

במחשב שעליו נכתב המאמר, השלב הזה לא הסתיים. ולידטור התגיות טוען את נתוני MITRE ATT&CK מהרשת. הבקשה החזירה HTTP 504 gateway timeout, והשגיאה חזרה בכל ניסיון. הפלט היה traceback שהסתיים בשורה RuntimeError: Failed to load MITRE ATT&CK data, וקוד היציאה היה 1.

זו תלות ברשת ולא שגיאה בחוק. אחרי שהנתונים נטענים, אמורה להופיע תוצאת מעבר או רשימת בעיות מהוולידטורים. אם הבדיקה נכשלת עם אותה הודעה על MITRE, אפשר להריץ שוב מאוחר יותר, או מרשת שמגיעה לנתונים במקור. אין לסמן את החוק כמאומת לפני שהבדיקה מסתיימת.

אם הבדיקה מדווחת על בעיות, צריך לקרוא כל אחת מהן ולתקן את החוק, ולא את הוולידטור. בעיה בתגית היא בדרך כלל טעות הקלדה, למשל attack.t1100 במקום attack.t1110.

שלב 4: התקנת הבקאנד של Splunk

bash
sigma plugin install splunk

במחשב שעליו נכתב המאמר, הפקודה הדפיסה Successfully installed plugin 'splunk' ואת pySigma version is compatible with sigma-cli.

שלב 5: המרה בלי pipeline

bash
sigma convert -t splunk --without-pipeline failed_logon.yml

הפלט היה:

text
Parsing Sigma rules
EventID=4625

כך נראה החוק לפני כל מיפוי ל-Splunk. שם השדה הוא עדיין השם של Sigma, EventID. בחיפוש ב-Splunk על אירועי Windows משתמשים בדרך כלל בשדה EventCode ובמקור, ולכן השאילתה הזו לא תמצא את האירועים הרצויים בהתקנה רגילה של ה-Windows add-on.

שלב 6: המרה עם ה-pipeline של Splunk

מנסים את אותה המרה בלי הדגל --without-pipeline:

bash
sigma convert -t splunk failed_logon.yml

הממיר עצר והסביר שהבקאנד של Splunk דורש processing pipeline. ההודעה אמרה למשתמש להוסיף את --without-pipeline רק אם הוא יודע מה הוא עושה. כאן ה-pipeline הוא חובה, ולכן הממיר מסרב להפיק שאילתה בלעדיו. כדאי לקרוא את ההודעה הזו כמנגנון בטיחות.

עכשיו מוסיפים את ה-pipeline של Windows:

bash
sigma list pipelines
sigma convert -t splunk -p splunk_windows failed_logon.yml

הרשימה הציגה שלושה pipelines של Splunk: splunk_windows, splunk_sysmon_acceleration, splunk_cim. ההמרה עם splunk_windows הדפיסה:

text
Parsing Sigma rules
source="WinEventLog:Security" EventCode=4625

ה-pipeline שינה שני דברים. הוא מיפה את EventID אל EventCode, והוסיף מגבלת מקור של WinEventLog:Security. התוצאה היא שאילתה שמופע Splunk עם ה-Windows add-on יכול להריץ. האינדקס וה-sourcetype אצלך עשויים להיות אחרים, אז זו נקודת התחלה, וצריך לבדוק אותה מול הנתונים שלך.

ניסיון נוסף עם ה-pipeline של CIM נכשל עם ההודעה Rule type not yet supported by the Splunk data model CIM pipeline!. לא כל חוק עובר המרה בכל pipeline. כשל כזה הוא מידע שימושי: החוק או הבקאנד לא מתאימים למיפוי שביקשת.

שלב 7: השוואה לאירוע שיצרת

במאמר על לוג האירועים של Windows בחנו אירוע ב-Event Viewer. פותחים את האירוע הזה שוב ומשווים אותו לשאילתה:

  • שדה Event ID ב-Event Viewer צריך להיות 4625. זה הערך שה-selection של Sigma מחפש.
  • שם השדה ב-Event Viewer הוא Event ID. בפלט של Splunk הוא מופיע בתור EventCode, כי ה-pipeline ממפה אותו לשם שבו משתמש ה-Windows add-on.
  • השדה source בשאילתה, עם הערך WinEventLog:Security, מקביל לערוץ Security שקראת ב-Event Viewer.

אם ה-Event ID של האירוע שלך ב-Event Viewer שונה מ-4625, החוק לא יתאים לו, וזו התנהגות נכונה. החוק עוסק בכשלי התחברות, ולא בכל אירוע בלוג.

בדיקת החוק

חוק שעובר המרה הוא עדיין ניחוש עד שבודקים אותו. לבדיקה המינימלית יש שני חצאים:

  • אירועי known-bad. אירועים שהחוק חייב להתאים להם. בחוק הזה מדובר באירועי 4625.
  • אירועי known-good. אירועים שהחוק לא אמור להתאים להם. התחברויות מוצלחות (4624), התנתקויות (4634) ויצירת תהליכים (4688) הם שליליים טובים.

בלי השליליים אין ראיה לכך שהחוק ספציפי. בלי החיוביים אין ראיה לכך שהוא עובד בכלל.

במעבדה הזו הבדיקה משתמשת בקובץ fixture קטן של אירועים סינתטיים, events.json, עם חמש רשומות: שני אירועי 4625 ושלושה אירועים שלא אמורים להתאים. הסקריפט טוען את בלוק selection של החוק ובודק כל אירוע. זו בדיקה מפושטת. היא מראה שלוגיקת ה-selection עושה את מה שכתבת, ואינה מחליפה בדיקה של השאילתה המומרת על נתונים אמיתיים.

python
import json, sys
import yaml

rule = yaml.safe_load(open("failed_logon.yml", encoding="utf-8"))
events = json.load(open("events.json", encoding="utf-8"))

selection = rule["detection"]["selection"]

def selection_matches(event):
    return all(str(event.get(k)) == str(v) for k, v in selection.items())

failures = 0
for ev in events:
    got = "match" if selection_matches(ev) else "no match"
    ok = got == ev["expect"]
    failures += 0 if ok else 1
    print(f"{'PASS' if ok else 'FAIL'}  {ev['name']:<40} expected={ev['expect']:<9} got={got}")

print(f"{len(events) - failures}/{len(events)} passed")
sys.exit(1 if failures else 0)

קובץ ה-fixture נראה כך:

json
[
  {"name": "known-bad: failed logon", "EventID": 4625, "expect": "match"},
  {"name": "known-bad: failed logon, second try", "EventID": 4625, "expect": "match"},
  {"name": "known-good: successful logon", "EventID": 4624, "expect": "no match"},
  {"name": "known-good: logoff", "EventID": 4634, "expect": "no match"},
  {"name": "known-good: process creation", "EventID": 4688, "expect": "no match"}
]

הרצה:

bash
python test_failed_logon.py

במחשב שעליו נכתב המאמר, הפלט היה:

text
PASS  known-bad: failed logon                  expected=match     got=match
PASS  known-bad: failed logon, second try      expected=match     got=match
PASS  known-good: successful logon             expected=no match  got=no match
PASS  known-good: logoff                       expected=no match  got=no match
PASS  known-good: process creation             expected=no match  got=no match
5/5 passed

שווה לשים לב מה הבדיקה הזו מראה ומה לא. היא מראה שהחוק מתאים לאירועים שהוא צריך להתאים להם ומתעלם מאלה שהוא צריך להתעלם מהם. היא לא מראה שהחוק טוב. חוק שהיה מתאים לכל 4625 היה עובר את הבדיקה, כי כל 4625 ב-fixture אמור להתאים. הבדיקה שימושית, אבל רק כרף תחתון.

הצעד הבא, בסביבה אמיתית, הוא להריץ את שאילתת ה-Splunk המומרת על פרק זמן של לוגי Windows Security שידוע שיש בו כשלי התחברות ופעילות רגילה. סופרים את התוצאות ופותחים מדגם מהן. כל תוצאה צריכה להיות אירוע 4625. אם השאילתה מחזירה תוצאות עם Event IDs אחרים, ה-pipeline או מיפוי השדות שגויים.

החוק עוד לא גמור

החוק הזה מתריע על כל 4625. סיסמה אחת שהוקלדה לא נכון יוצרת התראה, וכך גם חשבון שירות עם סיסמה שפג תוקפה ומנסה שוב כל דקה. ניסיון brute force הוא הרבה כשלים מול אותו חשבון בזמן קצר, והגרסה הראשונה לא יודעת לבטא את זה.

Sigma מטפל בזה באמצעות correlation rules, שמוגדרים במפרט נפרד. חוק correlation לא מתאים אירועים בעצמו. הוא מצביע על חוק בסיס אחד או יותר לפי name, מקבץ את ההתאמות שלהם לפי שדה, ומחיל ספירה על חלון זמן. הסוג event_count סופר התאמות, והביטוי condition: gte: 5 פירושו חמש או יותר. שמים את שני החוקים בקובץ אחד, מפרידים ביניהם עם ---, ונותנים לחוק הבסיס name:

yaml
title: Failed Logon Event
name: failed_logon_event
id: bd37e49d-15ab-4e88-a3fb-53594e831b27
status: experimental
description: Detects Windows Security event 4625 (an account failed to log on).
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4625
    condition: selection
falsepositives:
    - Users mistyping passwords
level: low
tags:
    - attack.credential-access
    - attack.t1110
---
title: Many Failed Logons For One Account
id: 3f6c1d52-8a0e-4b7e-9d2a-6c41e0b7a915
status: experimental
description: Five or more failed logons for the same target account within ten minutes.
correlation:
    type: event_count
    rules:
        - failed_logon_event
    group-by:
        - TargetUserName
    timespan: 10m
    condition:
        gte: 5
falsepositives:
    - Service accounts with an expired password
level: medium

שומרים בשם multi.yml וממירים עם אותו pipeline:

bash
sigma convert -t splunk -p splunk_windows multi.yml

הפלט היה:

text
Parsing Sigma rules
source="WinEventLog:Security" EventCode=4625

| bin _time span=10m
| stats count as event_count by _time TargetUserName

| search event_count >= 5

השורה הראשונה היא חוק הבסיס. שלוש השורות שאחריה הן ה-correlation: חלוקת האירועים לחלונות של עשר דקות, ספירה לפי TargetUserName, ושמירה של הקבוצות עם חמישה או יותר. המספרים 5 ו-10 דקות הם ערכים זמניים. צריך לבחור אותם לפי הנתונים שלך, ולבדוק את התוצאה מול אותם אירועי known-good ו-known-bad. לא כל בקאנד תומך בכל סוג של correlation, ולכן כדאי לעבור על הפלט של הממיר לפני שסומכים עליו.

כדאי לשמור את הגרסה הראשונה של החוק. היא קו הבסיס, וההשוואה לגרסה עם ה-correlation מראה אם הסף עוזר.

טעויות נפוצות

  • כתיבת חוק בלי שליליים. חוק שנבדק רק על חיוביים יכול להתאים להכול ועדיין לעבור. צריך להוסיף אירועי known-good בכוונה.
  • מתן שם לשדה לפי התווית ב-Event Viewer. ב-Sigma השם הוא EventID. ב-Splunk, אחרי ה-pipeline, השם הוא EventCode. ערבוב של שני השמות בחוק אחד גורם להחמצות שקטות.
  • המרה בלי pipeline ואמון בפלט. הדגל --without-pipeline הוא בחירה מכוונת ואינו קיצור דרך. הפלט עלול לא לרוץ בפלטפורמה שלך.
  • שימוש בערך level: high בחוק רועש. החומרה צריכה להתאים למשמעות של התראה. אירוע נפוץ בלי הקשר צריך להתחיל נמוך.
  • שכחה של השדה falsepositives. אם אי אפשר לנקוב במקרה תמים, כנראה שעדיין לא חשבו מספיק על הסביבה.
  • שינוי ה-id בעריכת הלוגיקה. ה-id צריך לזהות את החוק לאורך זמן. חוק חדש מקבל id חדש. חוק שנערך שומר על ה-id שלו.
  • התייחסות לבדיקה שעברה כהוכחה. הפקודה sigma check בודקת את מבנה הקובץ. היא לא בודקת אם הזיהוי מוצא את האירועים הנכונים.

איך ממשיכים מכאן

אחרי שיש חוק שעובר בדיקה, המרה והטסטים שלו, הצעדים הבאים פשוטים:

  • לכוונן את חוק ה-correlation מול נתונים אמיתיים: לשנות את הסף ואת חלון הזמן, ולהשוות את מספר ההתראות וה-false positives מול חוק הבסיס.
  • לקרוא את שאר מפרט Sigma כדי להכיר פילטרים, מודיפיירים כמו |contains וגם |endswith, ואת השדה category.
  • להמיר את אותו חוק לבקאנד שני, כמו Elastic או Splunk SPL2, ולהשוות בין השאילתות. כדאי לרשום איפה ה-pipeline משנה את הלוגיקה ואיפה הוא רק משנה שמות שדות.
  • לבנות סט fixture גדול יותר, כולל אירועים ממעבדה שבה הפעילות בשליטתך. כשלי התחברות מייצרים רק על חשבונות ומערכות בבעלותך, ורק במעבדה שמותר לך לבדוק.

בקצרה: חוק הוא טענה על אירועים. כותבים את הטענה, ממירים אותה, ואז מדגימים אותה עם אירועים שאמורים להתאים ואירועים שלא.

מקורות

מקורות

  1. SigmaHQ, Sigma specification
  2. SigmaHQ, Sigma rules documentation
  3. SigmaHQ, sigma-cli
  4. MITRE ATT&CK, T1110 Brute Force
  5. SigmaHQ, Sigma correlation rules specification

עוד בנושא זיהוי ובלו טים

זיהוי ובלו טים

מה זה SIEM? לוגים, שאילתות והתראות

SIEM אוסף לוגים, מנרמל אותם, מריץ חוקי קורלציה ומעלה התראות לטריאז׳. הצינור המלא עם דוגמה שאפשר להריץ.

מתחילים10 דקות קריאה