在 YARA-L 中建立多階段查詢

支援的國家/地區:

本文說明 YARA-L 中的多階段查詢如何讓您將一個查詢階段的輸出內容,直接做為後續階段的輸入內容。相較於單一的整體查詢,這個程序可讓您進一步控管資料轉換。

將多階段查詢與現有功能整合

多階段查詢可與 Google Security Operations 中的下列現有功能搭配使用:

  • 複合偵測規則:多階段查詢可輔助複合偵測規則。與複合規則不同,使用搜尋的多階段查詢可以即時傳回結果。

  • 時間範圍和多事件規則:您可以運用多階段查詢,比較資料中的不同時間範圍,藉此偵測異常狀況。舉例來說,您可以使用初始查詢階段,在一段時間內建立基準,然後使用後續階段,根據該基準評估近期活動。您也可以使用多事件規則建立類似的比較。

資訊主頁搜尋功能均支援 YARA-L 中的多階段查詢。

彙整有助於關聯多個來源的資料,為調查提供更多背景資訊。連結相關事件、實體和其他資料,即可調查複雜的攻擊情境。詳情請參閱「在搜尋中使用彙整」。

定義多階段 YARA-L 語法

設定多階段查詢時,請注意下列事項:

  • 限制階段:除了根階段外,多階段查詢必須包含 1 到 4 個具名階段。
  • 順序語法:請務必先定義已命名階段的語法,再定義根階段的語法。

建立多階段 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 != ""
  $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)
  match:
    $source, $target by day
  outcome:
    $exchanged_bytes = sum($total_bytes)
}

存取階段輸出內容

後續階段可使用階段欄位存取具名階段的輸出內容。階段欄位對應於階段的 matchoutcome變數,用法與統合式資料模型 (UDM) 欄位類似。

如要存取階段欄位,請使用下列語法:

$<stage name>.<variable name>

存取時間戳記 (選用)

如果具名階段使用跳躍、滑動或滾動視窗,請使用下列保留欄位,存取每個輸出資料列的視窗開始和視窗結束時間:

  • $<stage name>.window_start

  • $<stage name>.window_end

window_startwindow_end 是整數欄位,以 Unix 紀元以來經過的秒數表示。不同階段的視窗大小可能不同。

限制

多階段查詢有下列功能和結構限制:

結構和階段限制

  • 根階段:每個查詢只能有一個根階段。

  • 具名階段:最多支援四個具名階段。

  • 參照階段:階段只能參照同一個查詢中,邏輯上定義在其之前的階段。

  • 聯結:所有階段最多可有四個非資料表聯結。

  • 結果規定:每個具名階段 (根階段除外) 都必須包含 match 區段或 outcome 區段。outcome 部分不需要匯總。

視窗和相容性限制

  • 功能支援搜尋資訊主頁支援多階段查詢,但規則不支援。

  • 視窗類型:避免在單一查詢中混用不同視窗類型。

  • 視窗依附元件:使用躍點或滑動視窗的階段,無法依附於同樣使用躍點或滑動視窗的另一個階段。

  • 翻滾視窗大小:不同階段的翻滾視窗大小可能不同,但大小差異不得超過 720x。

範例:階段匯總差異

以下範例視窗設定不符合規定:

stage monthly_stats {
  metadata.event_type = "NETWORK_CONNECTION"
    $source = principal.hostname
    $target = target.ip
    $source != ""
    $target != ""
    $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)

  match:
    $source, $target by month

  outcome:
    $exchanged_bytes = sum($total_bytes)
}

$source = $monthly_stats.source
$target = $monthly_stats.target

match:
    $source, $target by minute

如果 monthly_stats 階段會按月匯總資料,而根階段會按分鐘匯總 monthly_stats 的輸出內容,則 monthly_stats 的每個資料列都會對應到根階段的 43,200 個資料列 (因為一個月有 43,200 分鐘)。

階段和查詢限制

多階段查詢中的每個階段都有下列限制:

  • 適用於單一階段查詢的大部分限制,也適用於每個階段:

    • 輸出規定:每個階段都必須輸出至少一個相符或結果變數 (階段欄位)。

    • 彙整視窗:彙整作業使用的視窗大小上限 (跳躍、滾動或滑動) 為 2 天。

    • 結果變數數量上限

      • 如果客戶未選擇允許較大的結果變數限制,則為 20

      • 如果客戶選擇允許較大的結果變數限制,則為 50

    • 跳躍視窗的最小和最大大小

    • 陣列值結果變數中的元素數量上限

  • 多階段查詢的限制與統計資料查詢相同:

    • 統計資料查詢:每小時 120 次查詢 (API 和 UI)

    • Google SecOps 的搜尋檢視畫面:每分鐘 100 個檢視畫面

    • 使用者介面和 EventService.UDMSearch API 支援多階段聯結,但 SearchService.UDMSearch API 不支援。使用者介面也支援不含聯結的多階段查詢。

活動和全域限制

事件數量上限:

多階段查詢可同時處理的事件數量有嚴格限制:

  • UDM 事件:最多可使用 2 個 UDM 事件。

  • 實體內容圖 (ECG) 事件:最多可有一個 ECG 事件。

全球查詢限制:

這些限制是平台全域的限制,可控管多階段查詢可回溯的時間長度,以及可傳回的資料量。

  • 查詢時間範圍:標準查詢的時間範圍上限為 30 天。

  • 結果集總大小上限為 10,000 個結果。

多階段查詢範例

本節的範例有助於說明如何建立完整的 YARA-L 多階段查詢。

範例:搜尋異常活躍的網路連線 (小時)

這個多階段 YARA-L 範例會找出網路活動高於正常值的 IP 位址配對,並以活動持續時間超過三小時的配對為目標。查詢包含兩個必要元件:具名階段 hourly_statsroot 階段。

hourly_stats 階段會搜尋網路活動量高的 principal.iptarget.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 Epoch 紀元時間開始算起,以整數表示): $hourly_stats.window_end

根階段會處理 hourly_stats 階段的輸出內容。系統會計算 principal.iptarget.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 配對 (sourcetarget) 每天交換的總位元組數,並傳回下列階段欄位 (對應於輸出資料列中的資料欄):

    • $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 != ""
  $total_bytes = cast.as_int(network.sent_bytes + network.received_bytes)
  match:
    $source, $target by day
  outcome:
    $exchanged_bytes = sum($total_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()), $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

從階段匯出未匯總的變數

具名階段可以包含未匯總的 outcome 區段。也就是說,在 outcome 區段中定義的變數會直接從階段輸出,讓後續階段將這些變數做為階段欄位存取,無須進行分組匯總。

範例:匯出未匯總的變數

這個範例說明如何匯出未匯總的變數。請注意以下邏輯:

  • 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 = cast.as_int(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))

在多階段查詢中實作視窗化

多階段查詢支援具名階段中的所有視窗類型 (跳躍、滑動和翻滾)。如果已命名的階段包含視窗,則可使用下列保留欄位,存取每個輸出資料列的視窗開始和視窗結束時間:

  • $<stage name>.window_start
  • $<stage name>.window_end

範例:跳躍視窗

以下範例說明如何在多階段查詢中使用跳躍視窗:

  • 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 和實體事件 (例如依事件類型篩選)。

如需建議做法的一般指南,請參閱「Yara-L 最佳做法」;如需有關聯結的特定資訊,請參閱「最佳做法」。

還有其他問題嗎?向社群成員和 Google SecOps 專業人員尋求答案。