建立多級式查詢
本文說明 YARA-L 中的多階段查詢,如何讓您將一個查詢階段的輸出內容,直接做為後續階段的輸入內容。相較於單一的整體查詢,這個程序可讓您進一步控管資料轉換作業。
將多級式查詢與現有功能整合
多階段查詢可與 Google Security Operations 中的下列現有功能搭配使用:
複合式偵測規則:多階段查詢可補足複合式偵測規則的不足,填補自動偵測和主動調查之間的落差。複合規則擅長在較長的時間範圍內,找出複雜的多事件關聯性,而多級式查詢則可讓分析師從這些偵測結果,轉向進行即時的疊代搜尋,立即驗證並掌握正在發生的威脅。
時間範圍和多事件規則:您可以比較資料中的不同時間範圍,使用多級式查詢偵測異常狀況。舉例來說,您可以使用初始查詢在一段時間內建立基準,然後使用後續查詢,根據該基準評估近期活動。您也可以使用多事件規則建立類似的比較。
資訊主頁和搜尋功能均支援 YARA-L 多階段查詢。
彙整有助於關聯多個來源的資料,為調查提供更多背景資訊。連結相關事件、實體和其他資料,即可調查複雜的攻擊情境。詳情請參閱「在搜尋中使用彙整」。
重要考量事項
設定多階段查詢時,請注意下列事項:
- 限制階段:除了根階段外,多級式查詢必須包含一到四個具名階段。
- 順序語法:請務必先定義已命名階段的語法,再定義根階段的語法。
實作多級式查詢時,建議您查看下列已知問題和建議的解決方法:
所有多級式查詢的行為都類似於統計資料搜尋查詢 (輸出內容包含匯總統計資料,而非未匯總的事件或資料表資料列)。
由於資料集大小的關係,在其中一側使用 UDM 和實體事件的聯結效能可能會降低。我們強烈建議盡可能篩選聯結兩側的 UDM 和實體事件 (例如依事件類型篩選)。
如需建議做法的一般指南,請參閱 YARA-L 2.0 最佳做法;如需有關聯結的特定資訊,請參閱最佳做法。
重要術語
就聯結而言,「視窗化階段」是指含有視窗的 match 區段。相反地,資料表階段不會輸出視窗。
建立多階段 YARA-L 查詢
如要建立多階段 YARA-L 查詢,請完成下列步驟。
階段結構和語法
依序前往「調查」>「搜尋」。定義查詢階段時,請遵循下列結構:
語法:使用下列語法為每個階段命名,並將其與其他階段分開:
stage <stage name> { }
大括號:將所有階段語法放在大括號 {} 內。
順序:定義根階段之前,請先定義所有具名階段的語法。
參照:每個階段都可以參照查詢中先前定義的階段。
根階段:查詢必須有根階段,會在所有具名階段處理完畢後處理。
以下範例階段 daily_stats 會收集每日網路統計資料:
stage daily_stats {
metadata.event_type = "NETWORK_CONNECTION"
$source = principal.hostname
$target = target.ip
$source != ""
$target != ""
match:
$source, $target by day
outcome:
$exchanged_bytes = sum(network.sent_bytes + network.received_bytes)
}
存取階段輸出內容
後續階段可使用階段欄位存取具名階段的輸出內容。階段欄位對應於階段的 match 和 outcome 變數,用法與統合式資料模型 (UDM) 欄位類似。
如要存取階段欄位,請使用下列語法:
$<stage name>.<variable name>
選用:存取時間戳記
如果具名階段使用跳躍、滑動或翻滾視窗,請使用下列保留欄位,存取每個輸出資料列的視窗開始和結束時間:
$<stage name>.window_start$<stage name>.window_end
window_start 和 window_end 是整數欄位,以 Unix 紀元以來經過的秒數表示。不同階段的視窗大小可能不同。
多級式查詢範例
本節的範例有助於說明如何建立完整的多階段 YARA-L 查詢。
範例:搜尋異常活躍的網路連線 (小時)
這個多階段 YARA-L 範例會找出網路活動高於正常值的 IP 位址配對,並以維持高活動量超過三小時的配對為目標。查詢包含兩個必要元件:具名階段 hourly_stats 和 root 階段。
hourly_stats 階段會搜尋網路活動量高的 principal.ip 和 target.ip 配對。
這個階段會傳回下列欄位的單一每小時值:
來源 IP (字串) 的統計資料:
$hourly_stats.src_ip目的地 IP 的統計資料 (字串):
$hourly_stats.dst_ip事件數的統計資料 (整數):
$hourly_stats.count接收位元組的標準差 (浮點數):
$hourly_stats.std_recd_bytes平均接收位元組數 (浮點數):
$hourly_stats.avg_recd_bytes以秒為單位的時段開始時間,自 Unix Epoch 紀元時間開始算起 (整數):
$hourly_stats.window_start以秒為單位的時段結束時間,自 Unix 紀元時間開始算起 (整數):
$hourly_stats.window_end
根階段會處理 hourly_stats 階段的輸出內容。系統會計算 principal.ip 和 target.ip 配對的統計資料,且活動會超過 $hourly_stats 指定的門檻。然後篩選出高活動量超過三小時的配對。
stage hourly_stats {
metadata.event_type = "NETWORK_CONNECTION"
$src_ip = principal.ip
$dst_ip = target.ip
$src_ip != ""
$dst_ip != ""
match:
$src_ip, $dst_ip by hour
outcome:
$count = count(metadata.id)
$avg_recd_bytes = avg(network.received_bytes)
$std_recd_bytes = stddev(network.received_bytes)
condition:
$avg_recd_bytes > 100 and $std_recd_bytes > 50
}
$src_ip = $hourly_stats.src_ip
$dst_ip = $hourly_stats.dst_ip
$time_bucket_count = strings.concat(timestamp.get_timestamp($hourly_stats.window_start), "|", $hourly_stats.count)
match:
$src_ip, $dst_ip
outcome:
$list = array_distinct($time_bucket_count)
$count = count_distinct($hourly_stats.window_start)
condition:
$count > 3
如果您在根階段中變更比對條件,如下所示,即可為多級式查詢引入每日的視窗式匯總。
match:
$src_ip, $dst_ip by day
範例:搜尋異常活躍的網路連線 (使用 Z 分數)
這項多級式查詢會使用 Z 分數計算 (測量與平均值的標準差數),比較每日平均網路活動與今天的活動。這項查詢可有效搜尋內部資產與外部系統之間異常高的網路活動。
必要條件:查詢時間範圍必須大於或等於 2 天,且包含當天,計算出的 Z 分數才會有效。
這項多級式查詢包含 daily_stats 階段和 root 階段,兩者會共同計算網路活動的 Z 分數:
daily_stats階段會執行初始每日匯總作業。這個階段會計算每個 IP 配對 (source和target) 每天交換的總位元組數,並傳回下列階段欄位 (對應於輸出資料列中的資料欄):$daily_stats.source:單數,字串$daily_stats.target:單數,字串$daily_stats.exchanged_bytes:單數、整數$daily_stats.window_start:單數、整數$daily_stats.window_end:單數、整數
根階段會彙整每個 IP 配對的
daily_stats階段輸出內容。這項指標會計算整個搜尋範圍內每日交換位元組的平均值和標準差,以及今天交換的位元組。系統會使用這三個計算值來判斷 Z 分數。輸出內容會列出今天所有 IP 配對的 Z 分數,並依遞減順序排序。
// Calculate the total bytes exchanged per day by source and target
stage daily_stats {
metadata.event_type = "NETWORK_CONNECTION"
$source = principal.hostname
$target = target.ip
$source != ""
$target != ""
match:
$source, $target by day
outcome:
$exchanged_bytes = sum(network.sent_bytes + network.received_bytes)
}
// Calculate the average per day over the time window and compare with the bytes exchanged today
$source = $daily_stats.source
$target = $daily_stats.target
$date = timestamp.get_date($daily_stats.window_start)
match:
$source, $target
outcome:
$today_bytes = sum(if($date = timestamp.get_date(timestamp.current_seconds()), cast.as_int($daily_stats.exchanged_bytes), 0))
$average_bytes = window.avg($daily_stats.exchanged_bytes)
$stddev_bytes = window.stddev($daily_stats.exchanged_bytes)
$zscore = ($today_bytes - $average_bytes) / $stddev_bytes
order:
$zscore desc
從階段匯出未匯總的變數
在一般的多級式查詢中,資料通常會透過匯總程序在各階段之間傳遞,這可能會將多個事件「摺疊」成單一摘要。不過,在某些情況下,您需要保留每個事件的特定詳細資料 (例如不重複的程序 ID 或特定指令列),且不能失去該精細程度。為支援這項功能,您可以直接匯出變數,不必使用分組函式。
具名階段可以包含未匯總的「outcome」部分。也就是說,在 outcome 區段中定義的變數會直接從階段輸出,後續階段可將這些變數做為階段欄位存取,無須進行分組彙整。
這項詳細資料有助於:
- 保留資料準確度:您可以將事件的確切屬性 (例如特定檔案路徑) 傳遞至下一個階段,不必使用人工「預留位置」匯總 (例如
max()或array_distinct())。 - 降低查詢複雜度:簡化 YARA-L 邏輯,不必為了將值從第 1 階段傳輸到第 2 階段,而使用
match區段或分組陳述式。 - 提升效能:略過匯總引擎後,系統就能更有效率地在各階段之間串流資料,進而縮短複雜的大量搜尋執行時間。
範例:匯出未匯總的變數
這個範例說明如何匯出未匯總的變數。請注意以下邏輯:
top_5_bytes_sent階段會搜尋網路活動最多的五個事件。top_5_bytes_sent階段會輸出下列階段欄位,對應輸出資料列中的資料欄:$top_5_bytes_sent.bytes_sent:單數、整數$top_5_bytes_sent.timestamp_seconds:單數、整數
root階段會計算網路活動量最高的五個事件,其最晚和最早的時間戳記。
stage top_5_bytes_sent {
metadata.event_type = "NETWORK_CONNECTION"
network.sent_bytes > 0
outcome:
$bytes_sent = network.sent_bytes
$timestamp_seconds = metadata.event_timestamp.seconds
order:
$bytes_sent desc
limit:
5
}
outcome:
$latest_timestamp = timestamp.get_timestamp(max($top_5_bytes_sent.timestamp_seconds))
$earliest_timestamp = timestamp.get_timestamp(min($top_5_bytes_sent.timestamp_seconds))
在多級式查詢中實作視窗化
在多級式偵測中,您可以透過時間範圍,為階段內的事件關聯性定義特定時間界線。將資料分割為離散的時間值區 (例如 5 分鐘的滑動時間區間或 1 小時的滾動式時間區間),即可找出模式,例如暴力攻擊或信標行為,這些模式只有在以時間範圍內的序列進行分析時才會顯示。
多階段查詢支援具名階段中的所有視窗類型 (跳躍、滑動和翻滾)。這有助於您在階段之間傳遞時間相關資料,例如使用第 1 階段找出高頻率事件時間範圍,並使用第 2 階段將該特定時間範圍與後續管理動作建立關聯。
如果已命名的階段包含視窗,您可以使用下列保留欄位,存取每個輸出資料列的視窗開始和視窗結束時間:
$stage_window_start:標示時間範圍開頭的 Unix 時間戳記。$stage_window_end:標示時間範圍結尾的 Unix 時間戳記。
如要進一步瞭解視窗化,請參閱「YARA-L 2.0 視窗化邏輯」。
常見用途
- 連續偵測:將偵測到登入失敗次數暴增的特定時間範圍,傳遞至第二階段,以尋找隨後不久的登入成功事件。
- 時間長度分析:比較初始攻擊階段的
$stage_window_start與後續階段的事件時間戳記,計算「遭入侵時間」。 - 歷來基準線:使用時間視窗比較目前事件計數與前一個時間視窗的輸出變數。
範例:跳躍視窗
以下範例說明如何在多級式查詢中使用跳躍視窗:
hourly_stats階段會搜尋在同一小時內網路活動量高的 IP 配對。hourly_stats會輸出下列階段欄位,對應輸出資料列中的資料欄:$hourly_stats.src_ip:單數,字串$hourly_stats.dst_ip:單數,字串$hourly_stats.count:單數、整數$hourly_stats.std_recd_bytes:單一浮點值$hourly_stats.avg_recd_bytes:單一浮點值$hourly_stats.window_start:單數、整數$hourly_stats.window_end:單數、整數
根階段會篩除高活動量超過 3 小時的 IP 配對。由於
hourly_stats階段使用了躍遷視窗,因此時數可能會重疊。
stage hourly_stats {
metadata.event_type = "NETWORK_CONNECTION"
$src_ip = principal.ip
$dst_ip = target.ip
$src_ip != ""
$dst_ip != ""
match:
$src_ip, $dst_ip over 1h
outcome:
$count = count(metadata.id)
$avg_recd_bytes = avg(network.received_bytes)
$std_recd_bytes = stddev(network.received_bytes)
condition:
$avg_recd_bytes > 100 and $std_recd_bytes > 50
}
$src_ip = $hourly_stats.src_ip
$dst_ip = $hourly_stats.dst_ip
$time_bucket_count = strings.concat(timestamp.get_timestamp($hourly_stats.window_start), "|", $hourly_stats.count)
match:
$src_ip, $dst_ip
outcome:
$list = array_distinct($time_bucket_count)
$count = count_distinct($hourly_stats.window_start)
condition:
$count > 3
多階段查詢中的內部聯結
內層聯結可讓您關聯不同階段或來源類型的資料,建立複雜的分析工作流程,例如比較即時事件與預先計算的統計基準。加入階段後,您可以使用有狀態資料 (例如中位數或查閱表) 擴充原始遙測資料,找出單一階段事件篩選器會錯過的離群值或多向量威脅。
多階段查詢的階段內和階段間都支援內部聯結。內層聯結功能支援下列類型:
- UDM 和 UDM:關聯兩組不同的安全事件 (例如比對登入事件和後續的檔案存取)。
- UDM 和 ECG:將事件資料與實體脈絡圖表資訊合併,以豐富身分或資產。
- UDM 和資料表:根據靜態或上傳的參照清單 (例如高價值資產清單或特定部門的 IP 範圍),加入即時事件。
以下範例說明如何在 UDM 事件和計算資料表階段之間設定不相符的聯結 (在 outcome 或 events 區段中執行聯結,而不是在 match 區段中)。這個模式可讓您執行統計異常偵測,如下列平均絕對離差 (MAD) 計算所示:
median階段:計算每個來源主機和目標 IP 配對傳送的位元組中位數。$median.host:單數,字串$median.target:單數,字串$median.median:單一浮點值
absolute_deviations階段:將每個 UDM 事件與中位數階段的對應資料列合併。這項指標可讓您計算每個個別事件相對於同類群組的位元組傳送絕對偏差。$absolute_deviations.host:單數,字串$absolute_deviations.target:單數,字串$absolute_deviations.absolute_deviation:單一浮點值
- 根階段:計算所有 UDM 事件的絕對偏差平均值,以建立異常狀況門檻。
範例:設定無比對加入
stage median {
metadata.event_type = "NETWORK_CONNECTION"
$host = principal.hostname
$target = target.ip
match:
$host, $target
outcome:
$median = window.median(network.sent_bytes, true)
}
stage absolute_deviations {
metadata.event_type = "NETWORK_CONNECTION"
$join_host = principal.hostname
$join_host = $median.host
$join_target = target.ip[0]
$join_target = $median.target
outcome:
$host = $join_host
$target = $join_target
$absolute_deviation = math.abs(network.sent_bytes - $median.median)
}
$host = $absolute_deviations.host
$target = $absolute_deviations.target
match:
$host, $target
outcome:
$mean_absolute_deviation = avg($absolute_deviations.absolute_deviation)
範例:視窗階段與資料表階段之間沒有相符的聯結
以下範例說明如何在多階段查詢中,設定視窗階段與資料表階段之間的不相符聯結。
hourly_stats階段會計算每個來源和目標主機配對以及每小時值區傳送的總位元組數。hourly_stats階段會輸出下列階段欄位,對應輸出資料列中的資料欄:$hourly_stats.source_host:單數,字串$hourly_stats.dst_host:單數,字串$hourly_stats.total_bytes_sent:單一浮點值$hourly_stats.window_start:單數、整數$hourly_stats.window_end:單數、整數
agg_stats階段會計算每個來源和目標主機配對每小時的位元組平均值和標準差。agg_stats會輸出下列階段欄位,對應輸出資料列中的資料欄:$agg_stats.source_host:單數,字串$agg_stats.dst_host:單數,字串$agg_stats.avg_bytes_sent:單一浮點值$agg_stats.stddev_bytes_sent:單一浮點值
根階段會將
hourly_stats中的每個資料列,與agg_stats中相同來源和目標主機配對的資料列聯結。針對每個來源和目標主機配對,系統會使用該主機配對值區的傳送總位元組數和匯總統計資料,計算 z 分數。
stage hourly_stats {
$source_host = principal.hostname
$dst_host = target.hostname
principal.hostname != ""
target.hostname != ""
match:
$source_host, $dst_host by hour
outcome:
$total_bytes_sent = sum(network.sent_bytes)
}
stage agg_stats {
$source_host = $hourly_stats.source_host
$dst_host = $hourly_stats.dst_host
match:
$source_host, $dst_host
outcome:
$avg_bytes_sent = avg($hourly_stats.total_bytes_sent)
$stddev_bytes_sent = stddev($hourly_stats.total_bytes_sent)
}
$source_host = $agg_stats.source_host
$source_host = $hourly_stats.source_host
$dst_host = $agg_stats.dst_host
$dst_host = $hourly_stats.dst_host
outcome:
$hour_bucket = timestamp.get_timestamp($hourly_stats.window_start)
$z_score = ($hourly_stats.total_bytes_sent - $agg_stats.avg_bytes_sent)/$agg_stats.stddev_bytes_sent
多級式查詢中的交叉聯結
使用 Google SecOps 搜尋或資訊主頁時,多級式查詢中的 cross join 可讓您比較個別 UDM 事件資料,以及在其他 YARA-L 階段計算的匯總統計資料。
在 YARA-L 中,cross join 關鍵字適用於只傳回一個資料列的階段。
如果是在限制為 1 的階段與另一個資料集 (例如 UDM 事件) 之間使用 cross join,階段的單一資料列輸出內容會附加至其他資料集的每個資料列。這項功能會提供整體統計資料,讓事件資料更加豐富。
範例:找出異常登入活動
以下範例會找出登入頻率高於正常值的使用者。這項指標的計算方式是比較每位使用者的登入次數 (使用 user_login_counts 階段) 與所有使用者的平均登入次數 (使用 total_users 階段)。搜尋結果會顯示登入次數異常的使用者。
接著,使用 cross join 關鍵字,將 total_users 階段的結果連結至 user_login_counts 階段的結果。
stage user_login_counts {
$user = principal.user.userid
metadata.event_type = "USER_LOGIN"
security_result.action = "ALLOW"
match:
$user
outcome:
$login_count = count(metadata.id)
}
stage total_users {
outcome:
$count = count($user_login_counts.user)
limit:
1
}
cross join $total_users, $user_login_counts
$login_count = $user_login_counts.login_count
$user = $user_login_counts.user
$tot_users = $total_users.count
// all users who logged in the same number of times are grouped together.
match:
$login_count
outcome:
$num_users = count($user)
$frequency_percent = (count($user) / max($tot_users) ) * 100
限制
多級式查詢有下列功能和結構限制:
結構需求
建構查詢時,請務必遵守下列結構規定:
- 根階段:每個查詢只能有一個根階段。
- 具名階段:最多支援四個具名階段。
- 參照階段:階段只能參照同一個查詢中,邏輯上定義在該階段之前的階段。
- cross join:cross join 只能參照傳回單一資料列的階段。您必須在參照的階段中加入限制 (
1),才能滿足這項規定。這項功能相當實用,因為您可以在每個個別事件列中附加單一全域統計資料 (例如最大值或平均值),以進行比較。 - 聯結:所有階段最多可有四個非資料表聯結。
- 結果規定:每個具名階段 (不含根階段) 都必須包含
match區段或outcome區段,其中outcome區段不需要彙整。
視窗和相容性限制
使用視窗和執行查詢時,請遵守下列限制:
- 支援功能:多級式查詢適用於搜尋和資訊主頁,但不支援規則。
- 視窗類型:避免在單一查詢中混用不同視窗類型。
- 視窗依附元件:使用躍點或滑動視窗的階段,不得依附於同樣使用躍點或滑動視窗的階段。
- 滾動式時間區間大小:不同階段的滾動式時間區間大小可能不同,但大小差異不得超過 720x。
示例:階段匯總差異 (無效)
下列設定無效,因為一個月有 44,640 分鐘 (44,640 / 1 > 720):
階段:monthly_stats { ... match: by month }
根目錄:match: by minute
範例:階段匯總差異 (有效)
如要修正這個問題,請確保各階段之間的比例較小。舉例來說,將每小時資料匯總成每日報表:
階段:daily_stats { ... match: by day }
根目錄:match: by hour
由於 24 (一天的小時數) 小於 720,系統可以有效將階段資料對應至根階段。
階段和查詢限制
多級式查詢中的每個階段都有特定限制。適用於單一階段查詢的大部分限制,也適用於每個階段:
- 輸出規定:每個階段都必須輸出至少一個相符或結果變數 (階段欄位)。
查詢時間範圍:
- 標準查詢:最多 30 天。
- 多級式查詢 (無比對聯結):最多 14 天。
視窗大小限制:視窗大小上限 (躍點、滑動或滾動) 取決於查詢是否包含聯結:
使用聯結:任何類型 (躍點、滑動或滾動) 的視窗大小上限為 2 天。詳情請參閱「搜尋聯結限制」。
沒有聯結 (單一事件):
跳躍和滑動視窗:最多 2 天。
滾動式回溯期:最長可達 30 天。
結果變數上限:
- 預設為 20
- 如果客戶選擇提高上限,則為 50 個
- 陣列限制:陣列值結果變數最多可有 10,000 個元素。
每筆查詢的事件限制:
- 最多兩個 UDM 事件
- 最多一個心電圖事件
- 最多兩個資料表
服務和效能限制
多階段查詢的限制與統計資料查詢相同:
- 統計資料查詢:每小時 120 次 (API 和使用者介面)。
- 搜尋檢視畫面:每分鐘 100 個檢視畫面。
- API 支援:Google SecOps 系統和
EventService.UDMSearchAPI 支援多階段聯結,但SearchService.UDMSearchAPI 不支援。您也可以在系統中執行不含聯結的多階段查詢。
活動和全域限制
您必須遵守下列活動和平台範圍限制:
事件數量上限
多階段查詢會嚴格限制可同時處理的事件數量:
- UDM 事件:最多可選取 2 個 UDM 事件。
- 實體內容圖 (ECG) 事件:最多只能有一個 ECG 事件。
全域查詢限制
這些平台層級的限制會控管多階段查詢傳回的資料量和回溯時間長度:
- 查詢時間範圍:標準查詢的時間範圍最長為 30 天。
- 結果集總數:結果集總數上限為 10,000 個結果。
還有其他問題嗎?向社群成員和 Google SecOps 專業人員尋求答案。