כללים של אירוע יחיד ושל כמה אירועים ב-YARA-L
במסמך הזה מוצגות שאילתות שנכתבו ב-YARA-L 2.0. כל דוגמה מראה איך לקשר בין אירועים בשפת הכללים של השאילתה כדי לזהות איומי אבטחה, לעקוב אחרי התנהגות של ישויות ולשפר את הזיהויים באמצעות לוגיקה עסקית.
אפשר להשתמש בדוגמאות כאבני הבניין של YARA-L 2.0, כולל זיהוי אירועים בודדים, התאמה של ביטויים רגולריים וסינון של טווח רשת. הדוגמאות האלה מאורגנות בקטגוריות פונקציונליות כדי לעזור לכם להתקדם מלוגיקה בסיסית לקורלציה מתקדמת של כמה אירועים ולזיהויים מורכבים.
תחביר בסיסי
הדוגמאות בקטע הזה מראות איך לקשר ביעילות בין אירועי UDM ואיך לבנות שאילתות בשפת הכללים.
שאילתה לגבי אירוע יחיד
תרחיש לדוגמה: זיהוי בסיסי של סוג אירוע ספציפי (לדוגמה, USER_LOGIN) בלי צורך לבצע קורלציה בחלון זמן.
לוגיקה מרכזית: משתמשת רק בקטעי האירועים והתנאים כדי לזהות מופע יחיד. כלל של אירוע יחיד יכול להיות:
- כל כלל שלא כולל את הקטע
match. - כלל עם קטע
matchוקטעconditionשבודק רק את קיומו של אירוע אחד (לדוגמה,$e, #e > 0, #e >= 1, 1 <= #e, 0 < #e).
דוגמה: חיפוש של התחברות ראשונית של משתמש
כלל
דוגמה לכלל הבא מחפשת אירוע של התחברות משתמש (USER_LOGIN) ומחזירה את הראשון שהיא מוצאת בנתוני הארגון שמאוחסנים בחשבון Google SecOps:
rule SingleEventRule {
meta:
author = "noone@altostrat.com"
events:
$e.metadata.event_type = "USER_LOGIN"
condition:
$e
}
חיפוש
בדוגמה הזו של חיפוש לא מצטבר, האירועים הבודדים מוצגים ישירות. השאילתה הזו לא דורשת קורלציה של אירועים, ולכן משתני אירועים כמו $e1 לא נכללים בה.
metadata.event_type = "USER_LOGIN"
מרכז שליטה
הלוגיקה של השאילתה הזו מתמקדת בהצגת אירועים ספציפיים ולא קשורים במצב הגולמי שלהם, ולכן היא לא משתמשת בקטעים match או outcome שנדרשים להדמיות בלוח הבקרה.
דוגמה: זיהוי התחברות תוך חמש דקות
כלל
בדוגמה הבאה מוצג כלל של אירוע יחיד שמשתמש בקטע match כדי למצוא משתמש עם אירוע התחברות אחד לפחות שמתרחש בחלון זמן של 5 דקות (5m). היא בודקת אם קיים אירוע של התחברות משתמש.
rule SingleEventRule {
meta:
author = "alice@example.com"
description = "windowed single event example rule"
events:
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user over 5m
condition:
#e > 0
}
חיפוש
בדוגמה הזו של חיפוש סטטיסטי, הפעילות מצטברת בחלונות מתגלגלים של חמש דקות (5m), והפלט הוא שורה אחת לכל משתמש בכל חלון. מכיוון שהשאילתה מתמקדת בספירות נפח לכל חלון, המשתנים event והקטעים condition מושמטים, כי התוצאות כוללות באופן מובנה אירוע אחד או יותר. בגרסה הזו נעשה שימוש בחלון מתגלגל במקום בחלון קופץ כדי לוודא שהתוצאות מוצגות בצורה נכונה בפלטפורמה.
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
match:
$user by 5m
מרכז שליטה
בדוגמה הבאה משולב קטע outcome לחישוב המספר הכולל של אירועים לכל משתמש, כדי להציג את הנתונים כערך סטטיסטי לאורך זמן. השאילתה משתמשת בחלון עוקב (tumbling window) במקום בחלון קצוב (hopping window) כדי לוודא שנקודות הנתונים ממופות לקטגוריות נפרדות שלא חופפות, וכך מספקת תצוגה חזותית ברורה יותר של מגמות בלוח הבקרה.
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
match:
$user by 5m
outcome:
$event_count = count(metadata.id)
שליחת שאילתות וכוונון
תרחיש שימוש: זיהוי חלונות svchost.exeשמופעלים מספרייה לא סטנדרטית.
לוגיקת מפתח: שלילה (not) בשילוב עם התאמה לביטוי רגולרי.
דוגמה: זיהוי תהליכים על סמך החרגות
כלל
הכלל הבא בודק אם יש דפוסים ספציפיים בנתוני האירועים, ויוצר זיהוי אם הוא מוצא את הדפוסים. הכלל הזה כולל משתנה $e1 למעקב אחר סוג האירוע ושדה UDM metadata.event_type. הכלל בודק אם יש מקרים ספציפיים של התאמות לביטויים רגולריים עם e1. כשמתרחש האירוע $e1, נוצר זיהוי. תנאי not נכלל בכלל כדי להחריג נתיבים מסוימים שאינם זדוניים. אפשר להוסיף עד not תנאים כדי למנוע תוצאות חיוביות כוזבות.
rule suspicious_unusual_location_svchost_execution
{
meta:
author = "Google Cloud Security"
description = "Windows 'svchost' executed from an unusual location"
yara_version = "YL2.0"
rule_version = "1.0"
events:
$e1.metadata.event_type = "PROCESS_LAUNCH"
re.regex($e1.principal.process.command_line, `\bsvchost(\.exe)?\b`) nocase
not re.regex($e1.principal.process.command_line, `\\Windows\\System32\\`) nocase
condition:
$e1
}
חיפוש
בדוגמה הזו מתבצע חיפוש לא מצטבר כדי להפיק אירועים נפרדים. החיפוש הזה לא דורש קורלציה של אירועים בכמה מופעים, ולכן משתני אירועים כמו $e1 לא נחוצים.
metadata.event_type = "PROCESS_LAUNCH"
re.regex(principal.process.command_line, `\bsvchost(\.exe)?\b`) nocase
not re.regex(principal.process.command_line, `\\Windows\\System32\\`) nocase
מרכז שליטה
התחביר הזה כולל את הקטעים match ו-outcome כדי לחשב את נפח האירועים לאורך זמן. הפונקציה timestamp.get_timestamp() מחלקת את התוצאות לקבוצות לפי יום כדי להציג את המגמה.
metadata.event_type = "PROCESS_LAUNCH"
re.regex(principal.process.command_line, `\bsvchost(\.exe)?\b`) nocase
not re.regex(principal.process.command_line, `\\Windows\\System32\\`) nocase
$date = timestamp.get_timestamp(metadata.event_timestamp.seconds)
match:
$date
outcome:
$event_count = count(metadata.id)
טווח הרשת והלוגיקה
תרחיש לדוגמה: סינון פעילות על סמך רשתות משנה ספציפיות של כתובות IP (CIDR) והתאמה מול כמה שמות מארחים אפשריים.
מושגים מרכזיים:
-
net.ip_in_range_cidr(): הפונקציה הזו בודקת אם כתובת IP נתונה כלולה בתת-רשת נתונה של Classless Inter-Domain Routing (CIDR) לצורך התאמה של תת-רשתות, ואם היא מכילה את האופרטור or למערכי מחרוזות. - אופרטור לוגי
OR: משמש לשילוב של כמה תנאים. התנאים בקטע event משולבים באופן משתמע עםANDהאופרטורORבודק כמה שמות מארחים אפשריים.
דוגמה: התאמה לאירוע יחיד (טווח כתובות IP)
כלל
בדוגמה הבאה מוצג כלל של אירוע יחיד שמחפש התאמות בין שני שמות מארחים ספציפיים לבין טווח ספציפי של כתובות IP:
rule OrsAndNetworkRange {
meta:
author = "noone@altostrat.com"
events:
// Checks CIDR ranges.
net.ip_in_range_cidr($e.principal.ip, "203.0.113.0/24")
// Detection when the hostname field matches either value using or.
$e.principal.hostname = /pbateman/ or $e.principal.hostname = /sspade/
condition:
$e
}
חיפוש
בדוגמה הבאה של שאילתה מזוהים אירועים שבהם כתובת IP ספציפית נמצאת בטווח CIDR מוגדר, ושם המארח תואם לדפוס משתמש ספציפי:
net.ip_in_range_cidr(principal.ip, "203.0.113.0/24")
principal.hostname = /pbateman/ or principal.hostname = /sspade/
מכיוון שמדובר בשאילתת חיפוש ולא בכלל זיהוי, האירוע כולו מוחזר אוטומטית אם המסננים מתאימים. בקטע match הנתונים מקובצים לפי principal.ip וprincipal.hostname. לא נדרש קטע condition, ומשמיטים את משתני האירוע ($e) כי לא מתבצעת קורלציה של אירועים.
מרכז שליטה
שאילתת הדוגמה הבאה מצביעה על צבירה של תוצאות על ידי קיבוץ של זוגות ייחודיים של כתובות IP ושמות מארחים:
net.ip_in_range_cidr(principal.ip, "203.0.113.0/24")
principal.hostname = /pbateman/ or principal.hostname = /sspade/
match:
principal.ip, principal.hostname
ביטויים רגולריים בשאילתות
תרחיש לדוגמה: חיפוש של תבניות גמישות של מחרוזות (למשל, דומיינים ספציפיים באימיילים) תוך התעלמות מאותיות רישיות. הפונקציה הזו נמצאת בשימוש בעיקר בחיפוש ובכללים.
לוגיקה מרכזית: הפונקציה /regex/ nocase משמשת להתאמות בסיסיות, והפונקציה re.regex() משמשת לניתוח שדות מורכב.
דוגמה: סינון אימיילים
כלל
בדוגמה הבאה של ביטוי רגולרי ב-YARA-L 2.0 מחפשים אירועים עם אימיילים שהתקבלו מהדומיין altostrat.com. מכיוון שהמחרוזת nocase נוספה להשוואה של המשתנה $host regex ולפונקציה regex, ההשוואות האלה לא תלויות באותיות רישיות.
rule RegexRuleExample {
meta:
author = "noone@altostrat.com"
events:
$e.principal.hostname = $host
$host = /.*HoSt.*/ nocase
re.regex($e.network.email.from, `.*altostrat\.com`) nocase
match:
$host over 10m
condition:
#e > 10
}
חיפוש
בממשק החיפוש, הלוגיקה הזו משמשת לחיפוש איומים ברמת דיוק גבוהה ולחקירת נתונים. במקום לחכות להתראה אוטומטית, אנליסטים יכולים להריץ שאילתה ידנית ב-UDM כדי לחשוף מקרים ספציפיים של שמות מארחים שתואמים למוסכמת מתן שמות, לצד דומיינים של אימייל שממוקדים למטרה מסוימת. זו השיטה העיקרית לאימות הנפח של האירועים האלה לפני הפיכתם לכלל זיהוי קבוע.
principal.hostname = $host
$host = /.*HoSt.*/ nocase
re.regex(network.email.from, `.*altostrat\.com`) nocase
match:
$host over 10m
```
מרכז שליטה
הלוגיקה הבאה מזהה דפוסים מעניינים על ידי צבירה של נתוני טלמטריה של hostname ו-email לקטגוריות של 10 דקות (10m). כשמשתמשים בלוגיקה הזו במרכז בקרה, אנליסטים יכולים לראות את תדירות התקשורת מנכסים ספציפיים (שמתאימים ל-host) לדומיין altostrat.com. התצוגה הזו חיונית למעקב אחרי מגמות של תנועת נתונים פנימית ולזיהוי הגורמים העיקריים לתנועת הנתונים בתשתית קריטית.
principal.hostname = $host
$host = /.*HoSt.*/ nocase
re.regex(network.email.from, `.*altostrat\.com`) nocase
match:
$host over 10m
```
דוגמה: ביטוי רגולרי של שם מארח
כלל
בדוגמה הבאה מזהים פעילות ביומן שבה החשבון הראשי hostname מזוהה כשרת אינטרנט (webserver) או כשרת פיתוח (devserver). הוא משתמש בביטוי רגולרי לא תלוי-רישיות כדי לוודא ששינויים במוסכמות למתן שמות לא יגרמו לפספוסים בזיהוי.
rule WebServerOrDevServerActivity {
meta:
author = "Alex"
description = "Detects events where the principal hostname is 'webserver' or 'devserver', ignoring case."
severity = "Informational"
events:
$e.principal.hostname = /webserver|devserver/ nocase
condition:
$e
}
חיפוש
בדוגמה הבאה, הביטוי principal.hostname = /webserver|devserver/ nocase תואם לשמות מארחים כמו "WebServer01", "devserver-test" ו-"MyWebServers". זהו תרחיש שימוש נפוץ לאיתור אירוע ספציפי.
// Use /regex/ followed by nocase for a case-insensitive match
principal.hostname = /webserver|devserver/ nocase
מרכז שליטה
הדוגמה הספציפית הזו לא מוצגת במרכז הבקרה, אבל הכלל הזה מספק התראות פעילות וזיהוי מתמשך. בניגוד ללוח בקרה שדורש בדיקה ידנית, המערכת הזו מבטיחה שכל מופע של פעילות בשרתים האלה יסומן ויתועד אוטומטית במנוע הזיהוי, כך שיהיה אפשר לחפש אותו באופן מיידי.
דוגמה: חיפוש ביומנים גולמיים
כלל
כשמשתמשים בחיפוש ידני כדי לבצע חקירות בנקודת זמן מסוימת, כללי הזיהוי מספקים ניטור טלמטרי רציף מסביב לשעון. אפשר להמיר שאילתת חיפוש מוצלחת לכלל YARA-L כדי להפוך את תהליך ההתראה לאוטומטי.
היתרונות העיקריים של כללים:
- התראות בזמן אמת: סימון אוטומטי של התאמות כשהן נכנסות למערכת.
- עקביות: אין יותר צורך להזין מחדש מונחי חיפוש באופן ידני.
- פעולות שקשורות לתוצאות: מוזנות ישירות לתצוגת הזיהוי לצורך תעדוף של אנליסטים ותגובה לאירועים.
חיפוש
אנליסטים בתחום האבטחה משתמשים לעיתים קרובות ב-regex כדי לחפש ביומנים גולמיים שלא עברו ניתוח ב-Google SecOps. הפעולה הזו מאפשרת התאמה גמישה לדפוסים כדי למצוא פריטים ספציפיים, גם אם הם לא מובנים או לא עברו אינדוקס באופן מלא. התחביר כולל לוכסנים:
raw = /host/
השאילתה הזו מחזירה כל שורה ביומן הגולמי שבה מופיע רצף התווים "host". דוגמאות לתוכן תואם ביומן הגולמי: "hostname": "myhost123".
מרכז שליטה
אין גרסה ייעודית של לוח הבקרה לסוג האירוע הספציפי הזה. כדי להציג את הזיהויים האלה באופן חזותי בקנה מידה נרחב, אפשר:
- ממפים את
metadata.event_typeלתרשים עמודות או לתרשים עוגה בכלי ליצירת לוחות בקרה. - אפשר לעקוב אחרי התדירות של האירועים האלה בחלונות זמן של 7, 30 או 90 ימים כדי לזהות חריגות בהתנהגות המשתמשים.
שדות חוזרים עם תנאים אוניברסליים
תרחיש לדוגמה: ביקורת על אירועים שמכילים רשימות של נתונים (שדות חוזרים) כדי לוודא שאין חריגים מהימנים. לדוגמה, אימות של כל כתובת IP שמשויכת להתחברות נמצאת מחוץ לטווח מאובטח מוכר.
לוגיקה מרכזית: השימוש באופרטור all כדי להעריך כל רכיב בשדה חוזר לפי תנאי ספציפי, והדגמה של האופן שבו הקצאת שדה חוזר למשתנה placeholder (לדוגמה, $ip) יוצרת זיהוי נפרד לכל ערך ייחודי ברשימה.
דוגמה: אימות כתובת IP של התחברות חשודה
כלל
הכלל הבא מחפש אירועי כניסה שבהם כל כתובות ה-IP של המקור לא תואמות לכתובת IP שידוע שהיא מאובטחת בטווח זמן של חמש דקות (5m).
rule SuspiciousIPLogins {
meta:
author = "alice@example.com"
events:
$e.metadata.event_type = "USER_LOGIN"
// Detects if all source IP addresses in an event do not match "100.97.16.0"
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// it will be detected since "100.97.16.1", "100.97.16.2",
// and "100.97.16.3" all do not match "100.97.16.0".
all $e.principal.ip != "100.97.16.0"
// Assigns placeholder variable $ip to the $e.principal.ip repeated field.
// There will be one detection per source IP address.
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// there will be one detection per address.
$e.principal.ip = $ip
match:
$ip over 5m
condition:
$e
}
חיפוש
metadata.event_type = "USER_LOGIN"
// Detects if all source IP addresses in an event do not match "100.97.16.0"
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// it will be detected since "100.97.16.1", "100.97.16.2",
// and "100.97.16.3" all do not match "100.97.16.0".
all principal.ip != "100.97.16.0"
// Assigns placeholder variable $ip to the $e.principal.ip repeated field.
// There will be one detection per source IP address.
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// there will be one detection per address.
principal.ip = $ip
match:
$ip over 5m
מרכז שליטה
metadata.event_type = "USER_LOGIN"
// Detects if all source IP addresses in an event do not match "100.97.16.0"
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// it will be detected since "100.97.16.1", "100.97.16.2",
// and "100.97.16.3" all do not match "100.97.16.0".
all principal.ip != "100.97.16.0"
// Assigns placeholder variable $ip to the $e.principal.ip repeated field.
// There will be one detection per source IP address.
// For example, if an event has source IP addresses
// ["100.97.16.1", "100.97.16.2", "100.97.16.3"],
// there will be one detection per address.
principal.ip = $ip
match:
$ip over 5m
חלונות מתקדמים
בקטע הזה מוסבר על דפוסים רב-שלביים ועל זיהויים שמופעלים על ידי פעילות מכללים אחרים.
קורלציה בין אירועים
בקטע הזה מוצגות דוגמאות לאופן שבו אפשר לעקוב אחרי ישויות (משתמשים או מארחים) בכמה אירועים או חלונות זמן כדי לזהות דפוסי התנהגות.
תרחיש שימוש: זיהוי של נסיעה בלתי אפשרית, שבה משתמש יחיד מתחבר לשירות משתי ערים או יותר תוך פחות מחמש (5m) דקות.
לוגיקה מרכזית: משתמשים בקטע match כדי לקבץ לפי $user ולפי #city > 1 כדי למצוא ערכי מיקום נפרדים.
דוגמה: זיהוי התחברות מיותר מיעד אחד
כלל
הכלל הבא מחפש משתמשים שנכנסו לחשבון הארגוני שלכם משתי ערים או יותר תוך פחות מ-5 (5m) דקות, כאשר $user הוא משתנה match, $udm הוא משתנה האירוע, ו-$city ו-$user הם משתני placeholder:
rule DifferentCityLogin {
meta:
events:
$udm.metadata.event_type = "USER_LOGIN"
$udm.principal.user.userid = $user
$udm.principal.location.city = $city
match:
$user over 5m
condition:
$udm and #city > 1
}
ההסבר הבא מתאר איך הכלל הזה פועל:
- מקבץ אירועים עם שם משתמש (
$user) ומחזיר אותו ($user) כשנמצאת התאמה. - טווח הזמן הוא חמש דקות (
5m); רק אירועים שההפרש ביניהם קטן מ-5 דקות (5m) נמצאים בקורלציה. - חיפוש קבוצת אירועים (
$udm) שסוג האירוע שלה הואUSER_LOGIN. - עבור קבוצת האירועים הזו, הכלל קורא למזהה המשתמש כ-
$userולעיר ההתחברות כ-$city. - הפונקציה מחזירה התאמה אם המספר הייחודי של ערכי
city(שמסומנים ב-#city) גדול מ-1בקבוצת האירועים ($udm) בטווח הזמן של 5 דקות (5m).
חיפוש
השאילתה לדוגמה הבאה מריצה חיפוש סטטיסטי מקביל כדי לזהות דפוסי נסיעה בלתי אפשרית. הוא מקבץ USER_LOGIN אירועים לפי משתמש בחלון של חמש דקות (5m) ומסנן את התוצאות כך שיוצגו רק מקרים שבהם זוהו כמה ערים שונות עבור זהות אחת.
events:
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
principal.location.city = $city
match:
$user over 5m
condition:
#city > 1
מרכז שליטה
שאילתת הדוגמה הבאה יוצרת תרשים מקביל בלוח הבקרה למעקב אחרי פריצות פוטנציאליות לחשבון. הוא צובר USER_LOGIN אירועים לפי משתמש בחלון של חמש דקות (5m) ומסנן מקרים שבהם זהות יחידה משויכת ליותר מעיר אחת (#city), וכך מאפשר לכם לשרטט את האנומליות הגיאוגרפיות האלה בסיכון גבוה לאורך זמן.
events:
metadata.event_type = "USER_LOGIN"
principal.user.userid = $user
principal.location.city = $city
match:
$user over 5m
condition:
#city > 1
יצירה ומחיקה מהירות של משתמשים
תרחיש לדוגמה: זיהוי של חשבונות זמניים שנוצרו ומחיקה שלהם תוך 4 שעות.
הלוגיקה העיקרית: צירוף של שני סוגי אירועים (
USER_CREATIONו-USER_DELETION) במשתנה משותף$userוהשוואה של חותמות הזמן.
דוגמה: יצירה ומחיקה מהירות של משתמשים
כלל
בדוגמה הבאה של כלל, המערכת מחפשת משתמשים שנוצרו ואז נמחקו תוך 4 שעות (4h), כאשר $create ו-$delete הם משתני האירוע, $user הוא המשתנה match ואין משתני placeholder:
rule UserCreationThenDeletion {
meta:
events:
$create.target.user.userid = $user
$create.metadata.event_type = "USER_CREATION"
$delete.target.user.userid = $user
$delete.metadata.event_type = "USER_DELETION"
$create.metadata.event_timestamp.seconds <=
$delete.metadata.event_timestamp.seconds
match:
$user over 4h
condition:
$create and $delete
}
חיפוש
בדוגמה הבאה מוצג חיפוש של נתונים סטטיסטיים של כמה אירועים, שמשמש לזיהוי שינויים מהירים במחזור החיים של החשבון. השאילתה הזו מחזירה שורה אחת לכל משתמש בחלון של ארבע שעות, ומתבצעת קורלציה בין יצירה למחיקה של זהויות.
חיפוש ברירת המחדל מחזיר כל חלון שמכיל את האירועים שצוינו, ולכן לא צריך להשתמש בקטע condition.
$create.target.user.userid = $user
$create.metadata.event_type = "USER_CREATION"
$delete.target.user.userid = $user
$delete.metadata.event_type = "USER_DELETION"
$create.metadata.event_timestamp.seconds <=
$delete.metadata.event_timestamp.seconds
match:
$user over 4h
מרכז שליטה
בדוגמה הבאה מוצג חיפוש במרכז בקרה עם כמה אירועים, שנועד לשרטט מגמות של מחזור החיים של החשבון לאורך זמן. באמצעות חלון עוקב (tumbling window) (by 4h), התוצאות ממופות למקטעי זמן נפרדים שלא חופפים, וזה אידיאלי להצגה חזותית.
הגרסה הזו כוללת קטע outcome לחישוב המספר הייחודי של אירועי יצירה בכל חלון. בניגוד לחיפוש הקודם, בגרסה הזו לא נדרש להחזיר משתני אירוע ספציפיים, כי המיקוד הוא בערכים סטטיסטיים מצטברים ולא בשורות יומן בודדות.
$create.target.user.userid = $user
$create.metadata.event_type = "USER_CREATION"
$delete.target.user.userid = $user
$delete.metadata.event_type = "USER_DELETION"
$create.metadata.event_timestamp.seconds <=
$delete.metadata.event_timestamp.seconds
match:
$user by 4h
outcome:
$event_count = count_distinct($create.metadata.id)
חלון הזזה בשאילתות
תרחיש לדוגמה: זיהוי בעיית אבטחה פוטנציאלית שבה אירוע ראשוני (מ-firewall_1) לא מלווה באירוע עוקב צפוי (מ-firewall_2) באותו מארח בתוך פרק זמן ספציפי.
הלוגיקה העיקרית:
- אירוע מרכזי: הכלל מתמקד באירועים מ-
firewall_1, שמסומנים כ-$e1. בכל פעם שמתרחש אירוע$e1, הוא משמש כנקודת ציר. - חלון זמן: בקטע
match($host over 10m after $e1) מוגדר חלון של 10 דקות שמתחיל מיד אחרי כל אירוע$e1. חלון הזמן הזה מתעדכן בכל פעם שמתרחש אירוע חדש מסוג$e1. - קורלציה: האירועים מקובצים לפי שם המארח (
$host). - תנאי לזיהוי (
$e1ו-$e2): זיהוי מופעל עבור מארח נתון אם:- מוצג אירוע מ
firewall_1($e1). -
AND, במהלך חלון של 10 דקות אחרי האירוע הספציפי$e1, נמצא אירועNOמ-firewall_2($e2) עבור אותו מארח.
- מוצג אירוע מ
דוגמה: זיהוי של אירועים עוקבים חסרים
כלל
בדוגמה הבאה מפורטים מקרים שבהם אירוע משני לא מתרחש אחרי טריגר ראשי. באמצעות התנאי !$e2 בטווח של 10 דקות, הכלל הזה מסמן טלמטריה חסרה – במיוחד כשיומן של חומת אש נראה במיקום אחד אבל לא מופיע בקפיצה הבאה הצפויה, מה שמצביע על פער פוטנציאלי בנראות או על ירידה בתנועה.
rule MissingSequentialEvent {
meta:
author = "alice@example.com"
events:
$e1.metadata.product_name = "firewall_1"
$e1.principal.hostname = $host
$e2.metadata.product_name = "firewall_2"
$e2.principal.hostname = $host
match:
// $e1 is the pivot; the 10-minute window starts at the $e1 timestamp
$host over 10m after $e1
condition:
$e1 and !$e2
}
חיפוש
בדוגמה הבאה מוצג חיפוש רציף שמשמש לזיהוי פערים בטלמטריה בין שני מקורות. החיפוש מתבצע באמצעות $e1 כנקודת ציר, ומחפש אירוע של חומת אש ראשית שלאחריו לא מופיע אירוע תואם בחומת אש שנייה תוך 10 דקות. זו דרך יעילה מאוד לחפש באופן ידני 'חורים שחורים' בתעבורת הרשת או כשלים ברישום ביומן במהלך חקירה.
$e1.metadata.product_name = "firewall_1"
$e1.principal.hostname = $host
$e2.metadata.product_name = "firewall_2"
$e2.principal.hostname = $host
match:
// $e1 is the pivot; the 10-minute window starts at the $e1 timestamp
$host over 10m after $e1
condition:
$e1 and !$e2
מרכז שליטה
בדוגמה הבאה מוצג ניתוח של פערי חשיפה שנועד לתצוגת לוח בקרה. על ידי צבירה של מקרים שבהם אירוע משני לא מתרחש אחרי אירוע ראשי, אפשר לראות את מהימנות צינור הנתונים של הרישום ביומן לאורך זמן. הצגת האירועים ה "חסרים" בתרשים עוזרת לזהות אזורים מתים קבועים בנראות של הרשת או בעיות בהגדרות בשמות מארחים ספציפיים.
$e1.metadata.product_name = "firewall_1"
$e1.principal.hostname = $host
$e2.metadata.product_name = "firewall_2"
$e2.principal.hostname = $host
match:
// $e1 is the pivot; the 10-minute window starts at the $e1 timestamp
$host over 10m after $e1
condition:
$e1 and !$e2
שאילתות שמשויכות לכמה אירועים
תרחיש לדוגמה: זיהוי פעילות בתדירות גבוהה או פעילות של ניסיונות פריצה על ידי מעקב אחרי ישות אחת (לדוגמה, משתמש או מארח) בכמה מקרים של אירוע בחלון זמן ספציפי.
לוגיקה מרכזית: משתמשת בקטע
matchכדי לקבץ אירועים לפי משתנה ספציפי, ובקטעconditionכדי לבדוק אם מספר האירועים חצה סף מסוים (לדוגמה,#e >= 10) בטווח הזמן המוגדר.
כלל טיפוסי של כמה אירועים כולל:
- משתני אירוע להבחנה בין אירועים.
matchקטע שמציין את טווח הזמן שבו צריך לקבץ את האירועים.- קטע
conditionשמציין את התנאי שיפעיל את הזיהוי והבדיקה של קיום כמה אירועים.
בחיפוש, שאילתות עם כמה אירועים מוגדרות על ידי יותר מאירוע אחד בשאילתה. יש שתי דרכים להגדיר את זה בכללים:
כמה אירועים: (לדוגמה,
event1 = successful login, event2 = failed login).טריגרים מבוססי-תנאים: התנאי מוגדר כך שהטריגר יופעל רק כשכמה אירועים עומדים בקריטריונים (לדוגמה,
event1 > 10). כלל מהסוג הזה צריך לכלול גם קטעoutcome.
דוגמה: זיהוי התחברות בתדירות גבוהה
כלל
הכלל הבא מחפש משתמש שהתחבר לפחות 10 פעמים בפחות מ-10 דקות:
rule MultiEventRule {
meta:
author = "noone@altostrat.com"
events:
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user over 10m
condition:
#e >= 10
}
חיפוש
בדוגמה הבאה נעשה שימוש בחיפוש נתונים סטטיסטיים של כמה אירועים כדי לזהות פעילות התחברות בתדירות גבוהה. הוא מסמן כל מקרה שבו משתמש יחיד יוצר 10 או יותר אירועי התחברות בפרק זמן של 10 דקות (10m).
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user by 10m
condition:
#e >= 10
מרכז שליטה
בדוגמה הבאה נעשה שימוש בחיפוש של כמה אירועים כדי לעקוב אחרי חשיפה פוטנציאלית של החשבון. הכלי מזהה מקרים שבהם משתמש ומארח ספציפיים חווים מספר ניסיונות כניסה שנכשלו ואחריהם ניסיון כניסה מוצלח, ומאפשר לכם לראות בזמן אמת דפוסי אימות בסיכון גבוה. כדי לעשות זאת, הכלי מבצע קורלציה בין ניסיונות הכניסה בחלון הזזה של 10 דקות.
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user by 10m
condition:
#e >= 10
שאילתות עם כמה אירועים ותוצאות מחושבות
תרחיש לדוגמה: הפעלת לוגיקה של משפט תנאי כדי להגדיר
risk_scoreעל סמך חומרת הנכס או נפח הרשת.לוגיקה מרכזית: המערכת משתמשת בקטע
outcomeכדי לחשב משתנים, ובקטע של התנאי כדי לסנן לפי המשתנים האלה.
דוגמה: ניסיון לפריצה בכוח ואחריו כניסה מוצלחת
בדוגמה הבאה נעשה שימוש בקטע outcome כדי לספור אירועים בחלון match. השאילתה הזו יוצרת את אותה פלט כמו שאילתת רגילה עם כמה אירועים, אבל היא מדגימה איך לשלב משתנים מחושבים בלוגיקה של הזיהוי.
כלל
rule PossibleBruteForceThenSuccessfulLogin {
meta:
author = "Alex"
description = "Detects multiple failed login attempts followed by a successful login for the same user and host within a 10-minute window."
severity = "High"
tactic = "Credential Access"
events:
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a time window: by 10m.
// The rule will evaluate all events matching $failed or $success that share the same $user and $hostname within any given 10-minute period.
$user, $hostname by 10m
outcome:
// Calculate aggregate values from the events within the match window.
$failed_login_count = count($failed.metadata.id)
$successful_login_count = count($success.metadata.id)
condition:
// The conditions that must be met *within each matched group* ($user, $hostname over 10m).
// - #failed >= 5: There must be 5 or more events matching the $failed criteria.
// - #success >= 1: There must be at least 1 event matching the $success criteria.
#failed >= 5 and #success >= 1
}
חיפוש
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
מרכז שליטה
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
דוגמה: התאמה בין מארחים על סמך חלון זמן
כלל
הכלל הבא בודק שני אירועים כדי לקבל את הערך של $hostname. אם הערך של $hostname תואם במשך תקופה של 5 דקות (5m), יוחל ציון חומרה. כשכוללים תקופת זמן בקטע match, הכלל בודק את התקופה שצוינה.
rule OutcomeRuleMultiEvent {
meta:
author = "Google Cloud Security"
events:
$u.udm.principal.hostname = $hostname
$asset_context.graph.entity.hostname = $hostname
$severity = $asset_context.graph.entity.asset.vulnerabilities.severity
match:
$hostname over 5m
outcome:
$risk_score =
max(
100
+ if($hostname = "my-hostname", 100, 50)
+ if($severity = "HIGH", 10)
+ if($severity = "MEDIUM", 5)
+ if($severity = "LOW", 1)
)
$asset_id_list =
array(
if($u.principal.asset_id = "",
"Empty asset id",
$u.principal.asset_id
)
)
$asset_id_distinct_list = array_distinct($u.principal.asset_id)
$asset_id_count = count($u.principal.asset_id)
$asset_id_distinct_count = count_distinct($u.principal.asset_id)
condition:
$u and $asset_context and $risk_score > 50 and not arrays.contains($asset_id_list, "id_1234")
}
חיפוש
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
outcome:
// Calculate aggregate values from the events within the match window.
$failed_login_count = count($failed.metadata.id)
$successful_login_count = count($success.metadata.id)
```
מרכז שליטה
// Define the first type of event: Failed Login
// We use $failed to represent any event matching these criteria.
$failed.metadata.event_type = "USER_LOGIN"
$failed.security_result.action = "FAIL"
// Extract common fields to correlate on
$failed.target.user.userid = $user
$failed.principal.hostname = $hostname
// Define the second type of event: Successful Login
// We use $success to represent any event matching these criteria.
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "ALLOW"
// Correlate using the same user and hostname placeholders
$success.target.user.userid = $user
$success.principal.hostname = $hostname
match:
// This section is key for multi-event rules. It groups events:
// - By the common placeholder variables: $user and $hostname.
// - Within a sliding time window: over 10m.
// The rule will evaluate all events matching $failed or $success that share
// the same $user and $hostname within any given 10-minute period.
$user, $hostname over 10m
outcome:
// Calculate aggregate values from the events within the match window.
$failed_login_count = count($failed.metadata.id)
$successful_login_count = count($success.metadata.id)
זיהויים משולבים
זיהויים מורכבים משפרים את זיהוי האיומים באמצעות כללים מורכבים. הכללים המורכבים האלה משתמשים בזיהויים מכללים אחרים כקלט. האפשרות הזו מאפשרת לזהות איומים מורכבים שכללים ספציפיים לא יכולים לזהות. מידע נוסף זמין במאמר סקירה כללית על זיהויים מורכבים.
סינון ברמת סיכון גבוהה
תרחיש לדוגמה: סינון של זיהויים קיימים לפי מאפיינים של סיכון גבוה, כמו פעילות שכוללת חשבונות אדמין.
לוגיקה מרכזית: פועלת על תוצאות או על שדות מטא-נתונים בתוך ממצאים קיימים.
זיהויים מורכבים של סינון בסיכון גבוה הם הצורה הפשוטה ביותר של זיהוי מורכב שפועל על שדות בתוצאות הזיהוי, כמו משתני תוצאה או מטא-נתונים של כללים. הם עוזרים לסנן את הזיהויים לפי תנאים שעשויים להצביע על סיכון גבוה יותר, כמו משתמש אדמין או סביבת ייצור.
דוגמה: זיהוי משתמש עם הרשאת אדמין
כלל
הכלל המורכב הבא מחפש גילויים קיימים שבהם השחקן מזוהה כמשתמש עם הרשאת אדמין, ומחיל ציון סיכון סטנדרטי.
rule composite_admin_detection {
meta:
rule_name = "Detection with Admin User"
author = "Google Cloud Security"
description = "Composite rule that looks for any detections where the actor is an admin user"
severity = "Medium"
events:
$rule_name = $d.detection.detection.rule_name
$principal_user = $d.detection.detection.variables["principal_users"]
$principal_user = /admin|root/ nocase
match:
$principal_user over 1h
outcome:
$risk_score = 75
$upstream_rules = array_distinct($rule_name)
condition:
$d
}
חיפוש
החיפוש הסטטיסטי הבא מזהה ומצטבר פעילות בחשבונות עם הרשאות גבוהות. הדוח נועד להציג את כל שמות הכללים הייחודיים שהפעילו זיהויים שקשורים למשתמשים עם הרשאות אדמין או הרשאות root.
בשילתה הספציפית הזו, חלון הזמן מוסר כדי לבצע ניתוח סטטיסטי יחיד על כל האיתורים בפרק הזמן שנבחר. בנוסף, מכיוון שמדובר בחיפוש לא מצטבר שמתמקד בנתוני זיהוי קיימים, לא נדרשים הקטע events, משתני האירועים והקטע condition.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_users"]
$principal_user = /admin|root/ nocase
match:
$principal_user
outcome:
$upstream_rules = array_distinct($rule_name)
מרכז שליטה
השאילתה הזו במרכז הבקרה מאפשרת לכם לראות באופן חזותי אילו כללים ספציפיים מזהים הכי הרבה פעמים פעילות שקשורה לאדמינים. הוא נועד לספק סקירה כללית ברמה גבוהה של מגמות הזיהוי בסביבה שלכם.
שימו לב לשינוי במשתנה match ובצבירה outcome בהשוואה לדוגמאות הקודמות. השאילתה הזו מקבצת את התוצאות לפי שם הכלל ומחשבת את מספר משתמשי האדמין שזוהו בכל כלל.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_users"]
$principal_user = /admin|root/ nocase
match:
$rule_name
outcome:
$admin_detections = count($principal_user)
צבירה וקביעת סף
תרחיש לדוגמה: זיהוי משתמשים או מארחים שמייצרים נפח גדול של התראות או צוברים ניקוד סיכון משמעותי לאורך זמן.
לוגיקת מפתח: משתמשת ב-sum() או ב-count_distinct() כדי לנתח נתוני זיהוי מצטברים.
כללי זיהוי מורכבים של צבירת נתונים מאפשרים לקבץ ממצאים של זיהוי על סמך מאפיינים משותפים, כמו שם מארח או שם משתמש, ולנתח את הנתונים המצטברים. אלה כמה תרחישי שימוש נפוצים:
- זיהוי משתמשים שמייצרים נפח גבוה של התראות אבטחה או סיכון מצטבר.
- זיהוי מארחים עם דפוסי פעילות חריגים על ידי צבירה של זיהויים קשורים.
דוגמה: צבירת סיכונים
כלל
הכלל הזה מצטבר את ציון הסיכון של משתמש יחיד בחלון של 48 שעות. המערכת מזהה משתמשים שהסיכון המצטבר שלהם בכמה זיהויים חורג מסף מסוים.
בלוגיקה המעודכנת הזו, detection.detection.outcomes מוחלף במשתני שדה של מיפוי, שמאחסנים גם את המשתנים match וגם את המשתנים outcome. בנוסף, משתנה התוצאה $principal_users מוסר כי כל זיהוי מכיל בדיוק ערך אחד של משתנה ההתאמה, שכבר נרשם.
rule composite_risk_aggregation {
meta:
rule_name = "Risk Aggregation Composite"
author = "Google Cloud Security"
description = "Composite detection that aggregates risk of a user over 48 hours"
severity = "High"
events:
$rule_name = $d.detection.detection.rule_name
$principal_user = $d.detection.detection.outcomes["principal_users"]
$risk = $d.detection.detection.risk_score
match:
$principal_user over 48h
outcome:
$risk_score = 90
$cumulative_risk = sum($risk)
$upstream_rules = array_distinct($rule_name)
condition:
$d and $cumulative_risk > 500
}
חיפוש
החיפוש הסטטיסטי הזה מצטבר נתוני זיהוי כדי לחשב את הסיכון הכולל של המשתמשים במשך תקופה של 48 שעות. הפלט הוא שורה אחת לכל משתמש ראשי בכל חלון, והוא מספק תצוגה כללית של הסיכון בחשבון לפי סוגים שונים של זיהוי.
בגרסה הזו, לא נדרשים משתני אירוע. מנוע הכללים מסנן באופן אוטומטי את הזיהויים ללא משתמש ראשי, אבל כדי לוודא שהתוצאות יכללו רק נתונים מאוכלסים, צריך להוסיף לחיפוש מסנן מפורש ($principal_user != ""). כברירת מחדל, השאילתה מחזירה תוצאות רק אם יש זיהוי אחד או יותר של הפרות אצל משתמש מסוים.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_user"]
$principal_user != ""
$risk = detection.detection.risk_score
match:
$principal_user over 48h
outcome:
$risk_score = 90
$cumulative_risk = sum($risk)
$upstream_rules = array_distinct($rule_name)
condition:
$cumulative_risk > 500
מרכז שליטה
הווריאציה הזו מיועדת במיוחד ללוחות בקרה, כדי להציג את סיכון המשתמש ופעילות הזיהוי לאורך זמן. הוא מצבר נתונים למאגרי מידע נפרדים, ולכן הוא אידיאלי להמחשת מגמות כמו נפח הכללים הייחודיים שהופעלו או המספר הכולל של זיהויים לכל משתמש.
בשילתא הזו, החלון משתנה מחלון קצוב (hop) לחלון עוקב (by 48h). כך מובטח שנקודות הנתונים ימופו לפלחי זמן לא חופפים, מה שמאפשר ליצור תרשימים של סדרות זמנים בצורה ברורה יותר. בדומה לחיפושים אחרים שלא כוללים צבירה, לא נדרשים משתני אירוע, והקטע outcome מורחב וכולל ספירות נפרדות של שמות הכללים ומזהי הזיהוי.
$rule_name = detection.detection.rule_name
$principal_user = detection.detection.variables["principal_user"]
$principal_user != ""
$risk = detection.detection.risk_score
match:
$principal_user by 48h
outcome:
$cumulative_risk = sum($risk)
$rule_count = count_distinct($rule_name)
$detection_count = count_distinct(detection.id)
condition:
$cumulative_risk > 500
צבירה של טקטיקות
תרחיש לדוגמה: זיהוי משתמשים שהפעילות שלהם הובילה לגילויים בכמה טקטיקות שונות של MITRE ATT&CK, מה שמצביע על מחזור חיים מתקדם של התקפה (לדוגמה, מעבר מגישה ראשונית לחילוץ נתונים).
לוגיקה מרכזית: המערכת משתמשת ב-count_distinct($tactic) כדי להפעיל את הכלל רק כשמשתמש חוצה סף מסוים של טקטיקות שונות בפרק זמן של 48 שעות.
דוגמה: צבירת טקטיקות של MITRE
כלל
rule composite_tactic_aggregation {
meta:
rule_name = "MITRE Tactic Aggregation Composite"
author = "Google Cloud Security"
description = "Composite detection that detects if a user has triggered detections over multiple mitre tactics."
severity = "Medium"
events:
$principal_user = $d.detection.detection.outcomes["principal_users"]
$tactic = $d.detection.detection.outcomes["mitre_tactic"]
$rule_name = $d.detection.detection.rule_name
match:
$principal_user over 48h
outcome:
$mitre_tactics_count = count_distinct($tactic)
$mitre_tactics = array_distinct($tactic)
$calculated_risk = 50 + (15 * $mitre_tactics_count)
$upstream_rules = array_distinct($rule_name)
condition:
$d and $mitre_tactics_count > 1 }
חיפוש
בדוגמה הבאה מוצגת וריאציה של חיפוש שמיועדת למפתחי אבטחה שצריכים ליצור קורלציה בין זיהויים קיימים ולהחיל שקלול דינמי של סיכונים. לוגיקת השאילתה הזו מחלצת טקטיקות של MITRE ATT&CK ומידע על משתמשים ממקור הנתונים detection, מקבצת את הפעילות לפי המשתמש הראשי ומחשבת ציון סיכון מותאם אישית על סמך המגוון של הטקטיקות שנצפו.
detection.detection.outcomes.key = "principal_users"
detection.detection.outcomes.key = "mitre_tactic"
$principal_user = detection.detection.outcomes["principal_users"]
$tactic = detection.detection.outcomes["mitre_tactic"]
$rule_name = detection.detection.rule_name
match:
$principal_user
outcome:
$mitre_tactics_count = count_distinct($tactic)
$mitre_tactics = array_distinct($tactic)
$upstream_rules = array_distinct($rule_name)
$calculated_risk = 50 + (15 * $mitre_tactics_count)
$risk_score = if($calculated_risk > 100, 100, $calculated_risk)
מרכז שליטה
בדוגמה הבאה מוצגת וריאציה של לוגיקת ניתוח הזיהוי שמתאימה ללוחות בקרה. כשמשתמשים בשאילתה הזו בלוחות הבקרה של Google SecOps, היא מאפשרת למפתחים להמחיש את המשתמשים בסיכון גבוה על ידי קורלציה של תוצאות זיהוי בכללים שונים. הלוגיקה מחלצת את המשתמש הראשי ואת הטקטיקות של MITRE, מצבירה את הממצאים ומחילת ציון סיכון מוגבל כדי לעזור לתעדף את מאמצי החקירה ישירות בווידג'ט של לוח הבקרה.
detection.detection.outcomes.key = "principal_users"
detection.detection.outcomes.key = "mitre_tactic"
$principal_user = detection.detection.outcomes["principal_users"]
$tactic = detection.detection.outcomes["mitre_tactic"]
$rule_name = detection.detection.rule_name
match:
$principal_user
outcome:
$mitre_tactics_count = count_distinct($tactic)
$mitre_tactics = array_distinct($tactic)
$upstream_rules = array_distinct($rule_name)
$calculated_risk = 50 + (15 * $mitre_tactics_count)
$risk_score = if($calculated_risk > 100, 100, $calculated_risk)
```
זיהויים מורכבים רציפים
תרחיש לדוגמה: זיהוי דפוסי תקיפה קריטיים שבהם סדר הפעולות הוא חיוני. לדוגמה, זיהוי התחברות מוצלחת לחשבון שמתרחשת רק אחרי סדרה של התראות על ניסיונות כניסה בכוח מאותה כתובת IP.
לוגיקה מרכזית: מתאם בין זיהוי קודם לבין אירוע UDM גולמי עוקב על ידי צירוף שלהם למשתנה משותף (לדוגמה, $bruteforce_ip) ושימוש בהשוואה של חותמות זמן כדי לוודא שהאירועים התרחשו ברצף הנכון.
זיהויים מורכבים רציפים מזהים דפוסים של אירועים קשורים שבהם רצף הזיהויים חשוב, כמו זיהוי של ניסיון כניסה בכוח, ואחריו כניסה מוצלחת. הדפוסים האלה יכולים לכלול כמה זיהויים בסיסיים או שילוב של זיהויים בסיסיים ואירועים.
דוגמה: ניסיון לכניסה בכוח ואחריו כניסה מוצלחת
כלל
הכלל המורכב הבא מזהה דפוסים של אירועים קשורים שבהם הסדר חשוב. הוא מחפש באופן ספציפי ניסיון לפריצה ל-Google Workspace, ואחריו אירוע של כניסה מוצלחת מאותה כתובת IP מקורית, בטווח של 24 שעות.
rule composite_bruteforce_login {
meta:
rule_name = "Bruteforce Login Composite"
author = "Google Cloud Security"
description = "Detects when an IP address associated with a Workspace brute force attempt successfully logs in"
severity = "High"
events:
$bruteforce_detection.detection.detection.rule_name = /Workspace Anomalous Failed Logins/
$bruteforce_ip = $bruteforce_detection.detection.detection.variables["principal_ips"]
$login_event.metadata.product_name = "login"
$login_event.metadata.product_event_type = "login_success"
$login_event.metadata.vendor_name = "Google Workspace"
$login_ip = $login_event.principal.ip
// Ensure the brute force detection and successful login occurred from the same IP
$login_ip = $bruteforce_ip
$target_account = $login_event.target.user.email_addresses
// Ensure the brute force detection occurred before the successful login
$bruteforce_detection.detection.detection_time.seconds < $login_event.metadata.event_timestamp.seconds
match:
$bruteforce_ip over 24h
outcome:
$risk_score = 90
$principal_users = array_distinct($target_account)
condition:
$bruteforce_detection and $login_event
}
חיפוש
$bruteforce_detection.detection.detection.rule_name = /Workspace Anomalous Failed Logins/
$bruteforce_ip = $bruteforce_detection.detection.detection.variables["principal_ips"]
$login_event.metadata.product_name = "login"
$login_event.metadata.product_event_type = "login_success"
$login_event.metadata.vendor_name = "Google Workspace"
$login_ip = $login_event.principal.ip
// Ensure the brute force detection and successful login occurred from the same IP
$login_ip = $bruteforce_ip
$target_account = $login_event.target.user.email_addresses
// Ensure the brute force detection occurred before the successful login
$bruteforce_detection.detection.detection_time.seconds < $login_event.metadata.event_timestamp.seconds
match:
$bruteforce_ip over 24h
outcome:
$principal_users = array_distinct($target_account)
condition:
$bruteforce_detection and $login_event
מרכז שליטה
לוחות הבקרה מתמקדים בהצגה חזותית של נתוני אירועים גולמיים, בעוד שהלוגיקה המורכבת של הזיהוי מבצעת קורלציה בין התראות זיהוי קיימות לבין אירועים עוקבים. הניתוח הרב-שכבתי הזה מותאם למנוע הזיהוי ולא לווידג'טים של לוחות בקרה בזמן אמת.
זיהויים מבוססי-הקשר
תרחיש לדוגמה: העשרת זיהויים קיימים באמצעות מודיעין איומים חיצוני כדי לוודא אם התראה כוללת ישויות זדוניות ידועות. לדוגמה, בדיקה אם כתובת IP שסומנה בזיהוי אבטחה מופיעה גם בפיד איומים של צומת יציאה גלובלית של TOR.
לוגיקה מרכזית: משתמשת בכללים מורכבים כדי לאחד בין ממצאי זיהוי לבין נתוני גרף GLOBAL_CONTEXT (לדוגמה, פידים של מודיעין איומים ב-Google Cloud) על ידי התאמה של מאפיין משותף כמו כתובת IP.
זיהויים מורכבים שמודעים להקשר מעשירים את הזיהויים בהקשר נוסף, כמו כתובות IP שנמצאות בפידים של איומים.
דוגמה: העשרה של מודיעין איומי סייבר
כלל
הכלל המורכב הבא מוסיף באופן אוטומטי הקשר נוסף מפיד המידע של TOR לזיהויים הקיימים. הוא מבצע קורלציה בין כתובת IP שזוהתה בזיהוי קודם לבין הפיד של צמתי היציאה של TOR, כדי להעלות את רמת החומרה ואת ציון הסיכון של הממצא.
rule composite_tor_enrichment {
meta:
rule_name = "Detection with IP from TOR Feed"
author = "Google Cloud Security"
description = "Adds additional context from the TOR intel feed to detections"
severity = "High"
events:
$rule_name = $d.detection.detection.rule_name
$gcti.graph.metadata.entity_type = "IP_ADDRESS"
$gcti.graph.metadata.vendor_name = "Google Cloud Threat Intelligence"
$gcti.graph.metadata.source_type = "GLOBAL_CONTEXT"
$gcti.graph.metadata.product_name = "GCTI Feed"
$gcti.graph.metadata.threat.threat_feed_name = "Tor Exit Nodes"
$detection_ip = $d.detection.detection.variables["principal_ips"]
$detection_ip = $gcti.graph.entity.ip
match:
$detection_ip, $rule_name over 1h
outcome:
$risk_score = 80
condition:
$d and $gcti
}
חיפוש
``` $rule_name = $d.detection.detection.rule_name
$gcti.graph.metadata.entity_type = "IP_ADDRESS" $gcti.graph.metadata.vendor_name = "Google Cloud Threat Intelligence" $gcti.graph.metadata.source_type = "GLOBAL_CONTEXT" $gcti.graph.metadata.product_name = "GCTI Feed" $gcti.graph.metadata.threat.threat_feed_name = "Tor Exit Nodes"
$detection_ip = $d.detection.detection.variables["principal_ips"] $detection_ip = $gcti.graph.entity.ip
match: $detection_ip, $rule_name over 1h
condition: $d and $gcti ```
מרכז שליטה
זיהוי של מקרים שמתרחשים בו-זמנית
תרחיש לדוגמה: זיהוי שילוב של טקטיקות קשורות שהופעלו על ידי אותה ישות בפרק זמן ספציפי. לדוגמה, זיהוי משתמש שהפעיל גם זיהוי של העלאת רמת הרשאה וגם זיהוי של גניבת נתונים תוך 48 שעות.
לוגיקה מרכזית: משתמשת בצורה של צבירה כדי ליצור קורלציה בין כמה סוגים שונים של זיהוי על ידי צירוף שלהם למשתנה משותף של ישות (לדוגמה, $pe_user) בקטע match.
זיהויים מורכבים של אירועים שמתרחשים בו-זמנית הם סוג של צבירה שיכולה לזהות שילוב של אירועים קשורים, כמו שילוב של זיהויים של העלאת הרשאות ושל חילוץ נתונים שהופעלו על ידי משתמש.
דוגמה: הסלמת הרשאות וגניבת נתונים שמתרחשות בו-זמנית
כלל
הכלל המורכב הבא מחפש רצף או שילוב ספציפיים של זיהויים – העלאת הרשאות ואחריה העברה לא מורשית של נתונים – שמשויכים לאותו משתמש בחלון של 48 שעות.
rule composite_privesc_exfil_sequential {
meta:
rule_name = "Privilege Escalation and Exfiltration Composite"
author = "Google Cloud Security"
description = "Looks for a detection sequence of privilege escalation followed by exfiltration."
severity = "High"
events:
$privilege_escalation.detection.detection.rule_labels["tactic"] = "TA0004"
$exfiltration.detection.detection.rule_labels["tactic"] = "TA0010"
$privesc_user = $privilege_escalation.detection.detection.variables["principal_users"]
$exfil_user = $exfiltration.detection.detection.variables["principal_users"]
$privesc_user = $exfil_user
$privilege_escalation.detection.detection_time.seconds < $exfiltration.detection.detection_time.seconds
match:
$privesc_user over 48h
outcome:
$risk_score = 75
$privesc_rules = array_distinct($privilege_escalation.detection.detection.rule_name)
$exfil_rules = array_distinct($exfiltration.detection.detection.rule_name)
condition:
$privilege_escalation and $exfiltration
}
חיפוש
$privilege_escalation.detection.detection.rule_labels["tactic"] = "TA0004"
$exfiltration.detection.detection.rule_labels["tactic"] = "TA0010"
$privesc_user = $privilege_escalation.detection.detection.variables["principal_users"]
$exfil_user = $exfiltration.detection.detection.variables["principal_users"]
$privesc_user = $exfil_user
$privilege_escalation.detection.detection_time.seconds < $exfiltration.detection.detection_time.seconds
match:
$privesc_user over 48h
outcome:
$privesc_rules = array_distinct($privilege_escalation.detection.detection.rule_name)
$exfil_rules = array_distinct($exfiltration.detection.detection.rule_name)
condition:
$privilege_escalation and $exfiltration
מרכז שליטה
ניהול תוצאות ומשתנים
בקטע הזה מופיעות דוגמאות לחישוב הסיכון ולנרמול הנתונים לצריכה במורד הזרם.
הקטע 'שאילתות עם outcome'
אפשר להוסיף את הקטע האופציונלי outcome בכלל YARA-L 2.0 כדי לחלץ מידע נוסף על כל זיהוי. בקטע condition אפשר גם לציין תנאים למשתני התוצאה. אפשר להשתמש בקטע outcome של כלל זיהוי כדי להגדיר משתנים לשימוש בהמשך. לדוגמה, אפשר להגדיר ציון חומרה על סמך נתונים מהאירועים שמנותחים.
למידע נוסף, קראו את המאמרים הבאים:
תנאים לתוצאה
תרחיש לדוגמה: סינון של זיהויים על סמך ציוני סיכון מחושבים כדי לצמצם את הרעש ולהבטיח שרק אירועים ברמת מהימנות גבוהה או ברמת חומרה גבוהה יפעילו התראות. האפשרות הזו שימושית להשבתת פעילות בסיכון נמוך שלא עומדת בסף עסקי ספציפי.
הלוגיקה העיקרית: מגדירה משתנים בקטע outcome באמצעות מתמטיקה מותנית (לדוגמה, הוספת סיכון על סמך גודל הקובץ או השעה ביום), ואז מפנה למשתנים האלה בקטע condition כדי להגביל את הזיהוי.
דוגמה: סינון לפי ציון סיכון מחושב
כלל
בקטע condition, אפשר להשתמש במשתני outcome שהוגדרו בקטע outcome. בדוגמה הבאה מוצג סינון של ציוני סיכון כדי לצמצם את הרעשים בזיהויים באמצעות תנאים של תוצאות.
rule OutcomeConditionalRule {
meta:
author = "alice@example.com"
description = "Rule that uses outcome conditionals"
events:
$u.metadata.event_type = "FILE_COPY"
$u.principal.file.size = $file_size
$u.principal.hostname = $hostname
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week($u.metadata.collected_timestamp.seconds)
outcome:
$risk_score =
if($file_size > 500*1024*1024, 2) + // Files 500MB are moderately risky
if($file_size > 1024*1024*1024, 3) + // Files over 1G get assigned extra risk
if($dayofweek=1 or $dayofweek=7, 4) + // Events from the weekend are suspicious
if($hostname = /highly-privileged/, 5) // Check for files from highly privileged devices
condition:
$u and $risk_score >= 10
}
חיפוש
metadata.event_type = "FILE_COPY"
principal.file.size = $file_size
principal.hostname = $hostname
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week(metadata.collected_timestamp.seconds)
outcome:
$risk_score =
if($file_size > 500*1024*1024, 2) + // Files 500MB are moderately risky
if($file_size > 1024*1024*1024, 3) + // Files over 1G get assigned extra risk
if($dayofweek=1 or $dayofweek=7, 4) + // Events from the weekend are suspicious
if($hostname = /highly-privileged/, 5) // Check for files from highly privileged devices
מרכז שליטה
השאילתה הזו מוסיפה את משתנה התוצאה $hostname כדי להמחיש אילו מארחים משויכים לכל ציון סיכון.
metadata.event_type = "FILE_COPY"
principal.file.size = $file_size
principal.hostname = $hostname
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week(metadata.collected_timestamp.seconds)
outcome:
$host = $hostname
$risk_score =
if($file_size > 500*1024*1024, 2) + // Files 500MB are moderately risky
if($file_size > 1024*1024*1024, 3) + // Files over 1G get assigned extra risk
if($dayofweek=1 or $dayofweek=7, 4) + // Events from the weekend are suspicious
if($hostname = /highly-privileged/, 5) // Check for files from highly privileged devices
שאילתה על אירוע יחיד עם תוצאה
תרחיש לדוגמה: העשרת זיהויים בנקודת זמן עם הקשר מיידי, כמו הקצאת תגי חומרה על סמך רשימות משתמשים או מאפייני קבצים, בלי לדרוש חלון זמן או קורלציה של אירועים.
לוגיקת מפתח: נעשה שימוש בקטע outcome בכלל שחסר בו קטע match. כך אפשר לחלץ מטא-נתונים ולבצע לוגיקה מותנית (לדוגמה, בדיקת משתמש מול רשימת הפניות) לכל אירוע בודד שעומד בקריטריונים.
דוגמה: תיוג חומרה בנקודת זמן מסוימת
כלל
בדוגמה הבאה אפשר לראות איך משתמשים בקטע outcome בכלל של אירוע יחיד כדי להגדיר משתנים לשימוש בהמשך, למשל הגדרת ציון חומרה על סמך המשתמש הספציפי וגודל הקובץ שמעורבים באירוע של העתקת קובץ.
rule OutcomeRuleSingleEvent {
meta:
author = "alice@example.com"
events:
$u.metadata.event_type = "FILE_COPY"
$u.principal.file.size = $file_size
$u.principal.hostname = $hostname
outcome:
$suspicious_host = $hostname
$admin_severity = if($u.principal.user.userid in %admin_users, "SEVERE", "MODERATE")
$severity_tag = if($file_size > 1024, $admin_severity, "LOW")
condition:
$u
}
חיפוש
בדוגמה הבאה מפורטים אירועים של יצירת קבצים, ובקטע outcome מוקצות באופן דינמי רמות חומרה לכל תוצאה. בניגוד לכללים שמבוססים על כמה אירועים, בחיפוש הזה לא נדרשים משתני אירוע או קטע match. במקום זאת, הוא מעבד כל יומן בנפרד כדי להפיק את 1 row per event, שמועשר בלוגיקה מותאמת אישית שמבוססת על גודל הקובץ והרשאות המשתמש.
metadata.event_type = "FILE_CREATION"
principal.file.size = $file_size
principal.hostname = $hostname
outcome:
$suspicious_host = $hostname
$admin_severity = if(principal.user.userid in %a1, "SEVERE", "MODERATE")
$severity_tag = if($file_size > 1024, $admin_severity, "LOW")
מרכז שליטה
במקרה הזה, לא מתאים להשתמש בגרסה של לוח הבקרה כי המטרה העיקרית היא לתייג אירועים בודדים ולהוסיף להם מידע. אפשר לצבור את האירועים האלה בלוח בקרה (למשל, לחשב את המספר הכולל של האירועים לפי תג חומרה), אבל אז לא יופיעו הפרטים המפורטים ברמת השורה שהחיפוש הזה נועד להציג.
חישוב של ציון סיכונים מבוסס-רשת
תרחיש שימוש: זיהוי העברות נתונים בסיכון גבוה על ידי חישוב הנפח המצטבר של תנועת הרשת בקבוצת אירועים. כך תוכלו לזהות איומים שבהם ערך סף להצגת נתונים הכולל חורג ממגבלה מסוימת (לדוגמה, 1024 בייט), ובו-זמנית לקחת בחשבון את חומרת הפגיעות של הנכסים הרלוונטיים.
לוגיקה מרכזית: נעשה שימוש בפונקציית הצבירה sum() בקטע outcome כדי לשלב בין sent_bytes לבין received_bytes בכל האירועים בחלון match. במקרה של כללים, השאילתה משתמשת במשפט if כדי להחיל ציון סיכון גבוה יותר אם הסכום הזה חורג מסף מוגדר.
דוגמה: כלל לחישוב של ציון סיכונים שמבוסס על רשת
כלל
בדוגמה הבאה מוצג איך להשתמש בקטע outcome כדי לחשב ציון סיכון דינמי על סמך פעילות ברשת. הכלל מסכם את סך הבייטים שהועברו בקבוצת אירועים, ומחיל עדיפות גבוהה יותר על התאמות שחורגות מערך סף להצגת נתונים ספציפי (1024 בייטים), תוך התחשבות בחומרת הפגיעות של הנכס המעורב.
rule OutcomeRuleMultiEvent {
meta:
author = "alice@example.com"
events:
$u.udm.principal.hostname = $hostname
$asset_context.graph.entity.hostname = $hostname
$severity = $asset_context.graph.entity.asset.vulnerabilities.severity
match:
$hostname over 5m
outcome:
$total_network_bytes = sum($u.network.sent_bytes) + sum($u.network.received_bytes)
$risk_score = if($total_network_bytes > 1024, 100, 50) +
max(
if($severity = "HIGH", 10)
+ if($severity = "MEDIUM", 5)
+ if($severity = "LOW", 1)
)
$asset_id_list =
array(
if($u.principal.asset_id = "",
"Empty asset id",
$u.principal.asset_id
)
)
$asset_id_distinct_list = array_distinct($u.principal.asset_id)
$asset_id_count = count($u.principal.asset_id)
$asset_id_distinct_count = count_distinct($u.principal.asset_id)
condition:
$u and $asset_context and $risk_score > 50 and not arrays.contains($asset_id_list, "id_1234")
}
חיפוש
בדוגמה הבאה מוצגת וריאציה של חיפוש שמבצעת קורלציה בין אירועים ברשת UDM לבין הקשר של נכס מתוך תרשים הקשר של ישויות (ECG). הוא משתמש בmatchחלון של 5 דקות כדי לצבור את תנועת הרשת לפי שם המארח, מחשב ציון סיכון על סמך נפח הנתונים וחומרת הפגיעות, ומחיל מסנן מותנה כדי להחריג מזהי נכסים ספציפיים ממערך התוצאות הסופי.
$u.udm.principal.hostname = $hostname
$asset_context.graph.entity.hostname = $hostname
$severity = $asset_context.graph.entity.asset.vulnerabilities.severity
match:
$hostname over 5m
outcome:
$total_network_bytes = sum($u.network.sent_bytes) + sum($u.network.received_bytes)
$risk_score = if($total_network_bytes > 1024, 100, 50) +
max(
if($severity = "HIGH", 10)
+ if($severity = "MEDIUM", 5)
+ if($severity = "LOW", 1)
)
$asset_id_list =
array(
if($u.principal.asset_id = "",
"Empty asset id",
$u.principal.asset_id
)
)
$asset_id_distinct_list = array_distinct($u.principal.asset_id)
$asset_id_count = count($u.principal.asset_id)
$asset_id_distinct_count = count_distinct($u.principal.asset_id)
condition:
$u and $asset_context and $risk_score > 50 and not arrays.contains($asset_id_list, "id_1234")
מרכז שליטה
בדוגמה הבאה מוצגת וריאציה של לוחות בקרה שבה נתוני טלמטריה של רשת בזמן אמת מועשרים בנתוני פגיעות של נכסים. השאילתה הזו מאפשרת למפתחים ליצור ווידג'טים ללוח בקרה שמציגים את רמות הסיכון של נכסים, על ידי התאמה של שמות מארחים בחלון הזזה של 5 דקות. הלוגיקה משנה באופן דינמי את ציון הסיכון על סמך תפוקת הרשת והפגיעות ברמת החומרה הכי גבוהה שנמצאה בנכס, וכך מספקת תצוגה של מערכות שעלולות להיות בסיכון, לפי סדר עדיפות.
$u.udm.principal.hostname = $hostname
$asset_context.graph.entity.hostname = $hostname
$severity = $asset_context.graph.entity.asset.vulnerabilities.severity
match:
$hostname over 5m
outcome:
$total_network_bytes = sum($u.network.sent_bytes) + sum($u.network.received_bytes)
$risk_score = if($total_network_bytes > 1024, 100, 50) +
max(
if($severity = "HIGH", 10)
+ if($severity = "MEDIUM", 5)
+ if($severity = "LOW", 1)
)
$asset_id_list =
array(
if($u.principal.asset_id = "",
"Empty asset id",
$u.principal.asset_id
)
)
$asset_id_distinct_list = array_distinct($u.principal.asset_id)
$asset_id_count = count($u.principal.asset_id)
$asset_id_distinct_count = count_distinct($u.principal.asset_id)
condition:
$u and $asset_context and $risk_score > 50 and not arrays.contains($asset_id_list, "id_1234")
ארגון מחדש של כלל outcome עם כמה אירועים (לפני הארגון מחדש)
תרחיש לדוגמה: כדי לשפר את ביצועי המערכת ולצמצם את זמן האחזור של העיבוד, אפשר להמיר כללים של כמה אירועים לכללים של אירוע יחיד. האפשרות הזו מתאימה במיוחד לכללים שתוכננו במקור עם קטע התאמה בלבד כדי להפעיל את קטע התוצאה, אבל לא דורשים בפועל קורלציה בין כמה אירועים נפרדים.
לוגיקה מרכזית: מסיר את הקטע match ואת כל הפונקציות המצטברות (לדוגמה, max(), sum() או count()) מהקטע outcome. במעבר הזה, הכלל משתנה מקבוצת אירועים לאורך זמן להערכה של כל אירוע בנפרד כשהוא מגיע.
match) וכללים מרובי-אירועים (כללים עם קטע match).
אפשר להשתמש בקטע outcome גם בכללים של אירוע יחיד (כללים בלי
אם עיצבתם בעבר כלל רב-אירועים רק כדי שתוכלו להשתמש בקטע התוצאה, אתם יכולים לשנות את הכללים האלה על ידי מחיקת הקטע match כדי לשפר את הביצועים. חשוב לדעת שמאחר שהכלל לא כולל יותר קטע match שחל על קיבוץ, יכול להיות שתקבלו יותר זיהויים.
דוגמה: שינוי מבנה של תוצאה (לפני השינוי)
כלל
בדוגמה הבאה מוצג כלל לתוצאה של כמה אירועים שמשתמש רק במשתנה אחד של אירוע. מנוע הכללים משתמש בקטע match, ולכן הוא צריך לקבץ אירועים בחלון של 5 דקות לפני שהוא מחשב את התוצאה. פעולה כזו צורכת יותר משאבים מאשר הערכה של אירוע יחיד.
rule OutcomeMultiEventPreRefactor {
meta:
author = "alice@example.com"
description = "Outcome refactor rule, before the refactor"
events:
$u.udm.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
$risk_score = max(if($hostname = "my-hostname", 100, 50))
condition:
$u
}
חיפוש
שאילתה מקבילה של נתונים סטטיסטיים
events:
$u.udm.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
$risk_score = max(if($hostname = "my-hostname", 100, 50))
condition:
$u
מרכז שליטה
events:
$u.udm.principal.hostname = $hostname
match:
$hostname over 5m
outcome:
$risk_score = max(if($hostname = "my-hostname", 100, 50))
condition:
$u
ארגון מחדש של כלל outcome מרובה אירועים (אחרי הארגון מחדש)
תרחיש לדוגמה: סיום האופטימיזציה של שאילתה כדי לשפר את מהירות העיבוד. הסרת הדרישה לקיבוץ מאפשרת לשאילתה להפעיל זיהוי באופן מיידי עם ההגעה של אירוע תואם יחיד, מה שהופך את מנוע הכללים ליעיל הרבה יותר.
לוגיקת המפתח: מחיקת הקטע match והסרת הפונקציה aggregate (לדוגמה, max()) מההקצאה של המשתנה outcome. הלוגיקה בתוך משפט ה-if נשארת זהה, אבל עכשיו היא מוחלת על אירוע יחיד ולא על קבוצה.
אפשר לשנות את מבנה השאילתה על ידי מחיקת הקטע match. הערה: צריך גם להסיר את הצבירה בקטע outcome כי השאילתה היא עכשיו שאילתה של אירוע יחיד. מידע נוסף על צבירות זמין במאמר בנושא צבירות של תוצאות.
דוגמה: Outcome refactor (: #outcome-post-refactor)
כלל
rule OutcomeSingleEventPostRefactor {
meta:
author = "alice@example.com"
description = "Outcome refactor rule, after the refactor"
events:
$u.udm.principal.hostname = $hostname
// We deleted the match section.
outcome:
// We removed the max() aggregate.
$risk_score = if($hostname = "my-hostname", 100, 50)
condition:
$u
}
חיפוש
events:
$u.udm.principal.hostname = $hostname
outcome:
$risk_score = if($hostname = "my-hostname", 100, 50)
מרכז שליטה
events:
$u.udm.principal.hostname = $hostname
outcome:
$risk_score = if($hostname = "my-hostname", 100, 50)
הקצאת פונקציה לערך פלייסהולדר
תרחיש לדוגמה: נרמול נתונים (לדוגמה, יצירת סטנדרטיזציה של דומיינים של כתובות אימייל) כדי לוודא שהקיבוץ בקטע ההתאמה מדויק.
לוגיקה מרכזית: מקצה את התוצאה של re.capture() או strings.concat() למשתנה placeholder.
דוגמה: הקצאת משתנה של placeholder לפונקציה
אפשר להקצות משתנה placeholder לתוצאה של קריאה לפונקציה ולהשתמש במשתנה ה-placeholder בקטעים אחרים של הכלל, כמו הקטע match, הקטע outcome או הקטע condition.
כלל
rule FunctionToPlaceholderRule {
meta:
author = "alice@example.com"
description = "Rule that uses function to placeholder assignments"
events:
$u.metadata.event_type = "EMAIL_TRANSACTION"
// Use function-placeholder assignment to extract the
// address from an email.
// address@website.com -> address
$email_to_address_only = re.capture($u.network.email.to , "(.*)@")
// Use function-placeholder assignment to normalize an email:
// address@-> address@company.com
$email_from_normalized = strings.concat(
re.capture($u.network.email.from , "(.*)@"),
"@company.com"
)
// Use function-placeholder assignment to get the day of the week of the event.
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week($u.metadata.event_timestamp.seconds)
match:
// Use placeholder (from function-placeholder assignment) in match section.
// Group by the normalized from email, and expose it in the detection.
$email_from_normalized over 5m
outcome:
// Use placeholder (from function-placeholder assignment) in outcome section.
// Assign more risk if the event happened on weekend.
$risk_score = max(
if($dayofweek = 1 or $dayofweek = 7, 10, 0)
)
condition:
// Use placeholder (from function-placeholder assignment) in condition section.
// Match if an email was sent to multiple addresses.
#email_to_address_only > 1
}
חיפוש
metadata.event_type = "EMAIL_TRANSACTION"
// Use function-placeholder assignment to extract the
// address from an email.
// address@website.com -> address
$email_to_address_only = re.capture(network.email.from , "(.*)@")
// Use function-placeholder assignment to normalize an email:
// address@??? -> address@company.com
$email_from_normalized = strings.concat(
re.capture(network.email.to , "(.*)@"),
"@company.com"
)
// Use function-placeholder assignment to get the day of the week of the event.
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week(metadata.event_timestamp.seconds)
match:
// Use placeholder (from function-placeholder assignment) in match section.
// Group by the normalized from email, and expose it in the detection.
$email_from_normalized over 5m
outcome:
// Use placeholder (from function-placeholder assignment) in outcome section.
// Assign more risk if the event happened on weekend.
$risk_score = max(
if($dayofweek = 1 or $dayofweek = 7, 10, 0)
)
condition:
// Use placeholder (from function-placeholder assignment) in condition section.
// Match if an email was sent to multiple addresses.
#email_to_address_only > 1
מרכז שליטה
בדוגמה הבאה מוצגת וריאציה של לוחות בקרה שעברה אופטימיזציה להצגה חזותית של נתונים מסוג סדרות זמן. השאילתה הזו משתמשת בחלון עוקב (tumbling window) של יום אחד, במקום בגרנולריות ברמת הדקה, ולכן היא יוצרת נקודות נתונים יציבות שלא חופפות, שמתאימות לשרטוט של ציוני סיכון לאורך תקופה ממושכת. הלוגיקה מבצעת נרמול של ישויות אימייל ומחיל משקלים גבוהים יותר של סיכון על עסקאות בסופי שבוע, וכך מספקת מגמה יומית ברורה של פעילות חשודה באימייל לצורך מעקב לטווח ארוך.
metadata.event_type = "EMAIL_TRANSACTION"
// Use function-placeholder assignment to extract the
// address from an email.
// address@website.com -> address
$email_to_address_only = re.capture(network.email.from , "(.*)@")
// Use function-placeholder assignment to normalize an email:
// address@??? -> address@company.com
$email_from_normalized = strings.concat(
re.capture(network.email.to , "(.*)@"),
"@company.com"
)
// Use function-placeholder assignment to get the day of the week of the event.
// 1 = Sunday, 7 = Saturday.
$dayofweek = timestamp.get_day_of_week(metadata.event_timestamp.seconds)
match:
// Use placeholder (from function-placeholder assignment) in match section.
// Group by the normalized from email, and expose it in the detection.
$email_from_normalized over 5m
outcome:
// Use placeholder (from function-placeholder assignment) in outcome section.
// Assign more risk if the event happened on weekend.
$risk_score = max(
if($dayofweek = 1 or $dayofweek = 7, 10, 0)
)
condition:
// Use placeholder (from function-placeholder assignment) in condition section.
// Match if an email was sent to multiple addresses.
#email_to_address_only > 1
אופטימיזציה וסינון
אופטימיזציה יעילה של כללים מסתמכת על סינון מדויק של נתונים כדי להבטיח שמנוע הזיהוי יעבד רק מידע משמעותי. החרגה של נתונים 'רועשים' או חלקיים יכולה לשפר באופן משמעותי את הביצועים של הכללים ולוודא שההתראות שנוצרות הן כאלה שאפשר לפעול לפיהן.
| נושא | דוגמאות |
|---|---|
| החרגה של ערך אפס | החרגה של ערך אפס מפורש ומרומז |
החרגה של ערך אפס
תרחיש לדוגמה: כדי לוודא שהכלל מדויק ולצמצם את מספר התוצאות החיוביות הכוזבות, צריך לסנן באופן מפורש מחרוזות ריקות, ערכי null או חשבונות placeholder גנריים (לדוגמה, Guest) שלא מספקים נתוני אבטחה שניתן לפעול לפיהם.
לוגיקה מרכזית: המערכת מסתמכת על הסינון המובנה של ערכי אפס במשתנים שמשמשים בקטע match של מנוע הכללים, ומשתמשת באופרטורים מפורשים של אי-שוויון (!= "") בשדות אחרים של אירועים כדי לוודא שרק נתונים מאוכלסים מפעילים זיהוי.
מנגנון הכללים מסנן באופן מרומז את ערכי האפס עבור כל משתני המיקום שמשמשים בקטע match. כדי להשבית את ההנחיות, משתמשים באפשרות allow_zero_values. עם זאת, בשדות אחרים של אירועים שמפנים אליהם, ערכים של אפס לא נכללים אלא אם מציינים במפורש תנאים כאלה. מידע נוסף זמין במאמר בנושא ערכים אפסיים בקטע 'התאמה'.
דוגמה: החרגה של ערך אפס מפורש ומשתמע
כלל
rule ExcludeZeroValues {
meta:
author = "alice@example.com"
events:
$e1.metadata.event_type = "NETWORK_DNS"
$e1.principal.hostname = $hostname
// $e1.principal.user.userid may be empty string.
$e1.principal.user.userid != "Guest"
$e2.metadata.event_type = "NETWORK_HTTP"
$e2.principal.hostname = $hostname
// $e2.target.asset_id cannot be empty string as explicitly specified.
$e2.target.asset_id != ""
match:
// $hostname cannot be empty string. The rule behaves as if the
// predicate, `$hostname != ""` was added to the events section, because
// `$hostname` is used in the match section.
$hostname over 1h
condition:
$e1 and $e2
}
חיפוש
צריך לציין במפורש ש-hostname לא יכול להיות מחרוזת ריקה, כי אין מסנן ערך אפס משתמע ל-placeholder בקטע match.
$e1.metadata.event_type = "NETWORK_DNS"
$e1.principal.hostname = $hostname
// $e1.principal.user.userid may be empty string.
$e1.principal.user.userid != "Guest"
$e2.metadata.event_type = "NETWORK_HTTP"
$e2.principal.hostname = $hostname
// $e2.target.asset_id and hostname cannot be empty string as explicitly specified.
$e2.target.asset_id != ""
$hostname != ""
match:
$hostname over 1h
מרכז שליטה
צריך לציין במפורש ש-hostname לא יכול להיות מחרוזת ריקה, כי אין מסנן ערך אפס משתמע ל-placeholder בקטע match.
$e1.metadata.event_type = "NETWORK_DNS"
$e1.principal.hostname = $hostname
// $e1.principal.user.userid may be empty string.
$e1.principal.user.userid != "Guest"
$e2.metadata.event_type = "NETWORK_HTTP"
$e2.principal.hostname = $hostname
// $e2.target.asset_id and hostname cannot be empty string as explicitly specified.
$e2.target.asset_id != ""
$hostname != ""
match:
$hostname over 1h
הבעיה עדיין לא נפתרה? קבלת תשובות מחברי הקהילה וממומחי Google SecOps.