YARA-L の単一イベント ルールとマルチイベント ルール

以下でサポートされています。

このドキュメントでは、YARA-L 2.0 で記述されたクエリを示します。各例は、クエリ ルール言語内でイベントを関連付けてセキュリティの脅威を特定し、エンティティの動作をモニタリングし、ビジネス ロジックで検出を強化する方法を示しています。

これらの例を、単一イベント検出、正規表現の一致、ネットワーク範囲のフィルタリングなど、YARA-L 2.0 の構成要素として使用します。これらの例は、基本的なロジックから高度なマルチイベント相関関係と複合検出まで進めることができるように、機能カテゴリに分類されています。

基本的な構文と基本

このセクションの例では、ルール言語内で UDM イベントを効果的に相関させ、クエリを構造化する方法を示します。

トピック 例
単一イベント クエリ 初回ユーザー ログイン検索、5 分間のログイン検出
クエリとチューニング 除外ベースのプロセス検出
ネットワーク範囲とロジック 単一イベントのマッチング(IP 範囲)
クエリ内の正規表現 メールのフィルタリング、ホスト名の正規表現、未加工ログの検索
共通条件を含む繰り返しフィールド 不審なログイン IP の検証

単一イベントのクエリ

ユースケース: 特定のイベントタイプ(USER_LOGIN など)の基本的な検出。時間枠にわたる相関は必要ありません。

キーロジック: イベント セクションと条件セクションのみを使用して、単一の発生を特定します。単一イベントルールは次のいずれかになります。

  • match セクションがないルール。
  • match セクションと condition セクションで、1 つのイベントが存在するかどうかのみを確認するルール(例: $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 セクションは使用しません。

例: 5 分間のログイン検出

ルール

次の例は、match セクションを使用して、5 分(5m)の時間枠内に少なくとも 1 つのログイン イベントが発生したユーザーを検索する単一イベントルールを示しています。ユーザーのログイン イベントが存在するかどうかをチェックします。

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
}

検索

この統計検索の例では、アクティビティを 5 分(5m)のタンブリング ウィンドウに集計し、ウィンドウごとにユーザーあたり 1 行を出力します。クエリはウィンドウあたりのボリューム カウントに焦点を当てているため、結果には 1 つ以上のイベントが本質的に含まれるため、event 変数と condition セクションは省略されています。このバージョンでは、ホップ ウィンドウの代わりにタンブリング ウィンドウを使用して、プラットフォーム内で結果が正しくレンダリングされるようにしています。

metadata.event_type = "USER_LOGIN"
principal.user.userid = $user

match:
  $user by 5m

ダッシュボード

次の例では、outcome セクションを組み込んで、ユーザーあたりのイベントの合計数を計算しています。これにより、データを統計値として経時的にプロットできます。このクエリでは、ホップ ウィンドウではなくタンブリング ウィンドウを使用して、データポイントが重複しない個別のバケットにマッピングされるようにし、ダッシュボードの傾向をより明確に可視化します。

metadata.event_type = "USER_LOGIN"
principal.user.userid = $user

match:
  $user by 5m

outcome:
  $event_count = count(metadata.id)

クエリとチューニング

ユースケース: 標準以外のディレクトリから起動する Windows svchost.exe を検出します。

主なロジック: 否定(not)と正規表現の一致を組み合わせます。

例: 除外ベースのプロセス検出

ルール

次のルールは、イベントデータ内の特定のパターンをチェックし、パターンが見つかった場合は検出を作成します。このルールには、イベントタイプと metadata.event_type UDM フィールドを追跡するための変数 $e1 が含まれています。このルールは、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() 関数を使用し、複数のホスト名候補に対してチェックするには OR 演算子を使用します。

  • net.ip_in_range_cidr(): この関数は、指定された IP アドレスが指定されたクラスレス ドメイン間ルーティング(CIDR)サブネットに含まれているかどうかを確認します。
  • 論理演算子 OR: 複数の条件を結合するために使用されます。イベント セクション内の条件は、暗黙的に AND と組み合わされます。OR 演算子は、複数の可能なホスト名に対してチェックを行います。

例: 単一イベントのマッチング(IP 範囲)

ルール

次の例は、2 つの特定のホスト名と特定の 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
}

検索

Search インターフェースでは、このロジックは忠実度の高い脅威ハンティングとデータ探索に使用されます。自動アラートを待つのではなく、アナリストは 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

ダッシュボード

この特定の例はダッシュボードに可視化されていませんが、このルールはアクティブなアラートと永続的な検出を提供します。手動での確認が必要なダッシュボードとは異なり、この方法では、これらのサーバー上のすべてのアクティビティ インスタンスが自動的にフラグ設定され、検出エンジンに記録されて、すぐに検索できるようになります。

ルール

手動検索は特定の時点の調査に使用しますが、検出ルールは 24 時間 365 日のテレメトリー モニタリングを提供します。成功した検索クエリを YARA-L ルールに変換して、アラート プロセスを自動化できます。

ルールの主なメリットは次のとおりです。

  • リアルタイムのアラート: 一致がシステムに入力されると、自動的にフラグが付けられます。
  • 永続性: 検索キーワードを手動で再入力する必要がなくなります。
  • 結果アクション: アナリストのトリアージとインシデント対応の検出ビューに直接フィードされます。

検索

セキュリティ アナリストは、Google SecOps 内の未加工の未解析ログを検索するために regex を頻繁に使用します。このアクションにより、完全に構造化またはインデックス登録されていない場合でも、特定のアーティファクトを見つけるための柔軟なパターン マッチングが可能になります。構文ではスラッシュを使用します。

raw = /host/

このクエリは、文字列 "host" が出現する未加工のログ行を返します。一致する未加工ログのコンテンツの例としては、"hostname": "myhost123" などがあります。

ダッシュボード

この特定のイベントタイプ専用のダッシュボード バリアントはありません。これらの検出結果を大規模に可視化するには、次の方法があります。

  • ダッシュボード ビルダー内で metadata.event_type を棒グラフまたは円グラフにマッピングします。
  • これらのイベントの頻度を 7 日間、30 日間、90 日間の期間で追跡し、ユーザーの行動の異常を特定します。

ユニバーサル条件を含む繰り返しフィールド

ユースケース: データのリスト(繰り返しフィールド)を含むイベントを監査して、信頼できる例外が存在しないことを確認します。たとえば、ログインに関連付けられているすべての IP アドレスが既知の安全な範囲外であることを確認します。

キーロジック: all 演算子を使用して、繰り返しフィールドのすべての要素を特定の条件に対して評価します。また、繰り返しフィールドをプレースホルダ変数($ip など)に割り当てることで、リスト内の一意の値ごとに個別の検出が作成される仕組みを示します。

例: 不審なログイン IP の検証

ルール

次のルールは、5 分(5m)以内にすべての送信元 IP アドレスが安全であることがわかっている IP アドレスと一致しないログイン イベントを検索します。

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

高度なウィンドウ処理

このセクションでは、多段式パターンと、他のルールの動作によってトリガーされる検出について説明します。

トピック 例
マルチイベントの関連付け 複数都市からのログインの検出、ユーザーの迅速な作成と削除
クエリのスライディング ウィンドウ 欠落しているシーケンシャル イベントの検出
マルチイベント クエリ 高頻度ログインの検出
計算された結果を含む複数イベントのクエリ ブルート フォースに続くログイン成功、時間枠付きホスト マッチング

マルチイベントの相関分析

このセクションでは、複数のイベントまたは時間枠にわたってエンティティ(ユーザーまたはホスト)を追跡し、行動パターンを特定する方法の例を示します。

ユースケース: 1 人のユーザーが 5 分(5m)以内に 2 つ以上の都市からログインする不可能な移動を検出します。

キーロジック: match セクションを使用して、$user と #city > 1 でグループ化し、個別の位置情報の値を見つけます。

例: 複数都市でのログインの検出

ルール

次のルールは、5 分(5m)以内に 2 つ以上の都市から企業にログインしたユーザーを検索します。ここで、$user は match 変数、$udm はイベント変数、$city と $user はプレースホルダ変数です。

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)を返します。
  • 期間は 5 分(5m)です。5 分(5m)未満の間隔のイベントのみが関連付けられます。
  • イベントタイプが USER_LOGIN のイベント グループ($udm)を検索します。
  • そのイベントグループについて、ルールはユーザー ID を $user として、ログインの都市を $city として呼び出します。
  • 5 分間(5m)の期間内にイベント グループ($udm)で city 値の固有の数(#city で示される)が 1 より大きい場合に一致を返します。

検索

次のクエリ例は、同等の統計検索を実行して、不可能な移動パターンを特定します。USER_LOGIN イベントを 5 分(5m)のウィンドウ内でユーザー別にグループ化し、結果をフィルタして、1 つの ID に対して複数の異なる都市が検出されたインスタンスのみを表示します。

events:
  metadata.event_type = "USER_LOGIN"
  principal.user.userid = $user
  principal.location.city = $city

match:
  $user over 5m

condition:
  #city > 1

ダッシュボード

次のクエリの例は、アカウント侵害の可能性を追跡するための同等のダッシュボードの可視化を示しています。このルールは、5 分間(5m)のウィンドウでユーザーごとに USER_LOGIN イベントを集計し、1 つの ID が複数の異なる都市(#city)に関連付けられているインスタンスをフィルタします。これにより、これらの高リスクの地理的異常を時系列でプロットできます。

events:
  metadata.event_type = "USER_LOGIN"
  principal.user.userid = $user
  principal.location.city = $city

match:
  $user over 5m

condition:
  #city > 1

迅速なユーザーの作成と削除

ユースケース: 4 時間以内に作成されて削除された使い捨てアカウントを特定します。

主なロジック: 共有の $user 変数で 2 つのイベントタイプ(USER_CREATION と USER_DELETION)を結合し、タイムスタンプを比較します。

例: 迅速なユーザーの作成と削除

ルール

次のルール例では、作成されてから 4 時間以内に削除されたユーザーを検索します(4h)。ここで、$create と $delete はイベント変数、$user は match 変数であり、プレースホルダ変数はありません。

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
}

検索

次の例は、アカウント ライフサイクルの急速な変化を特定するために使用されるマルチイベント統計検索を示しています。このクエリは、4 時間のウィンドウ内のユーザーごとに 1 行を出力し、ID の作成と削除を関連付けます。

検索ではデフォルトで指定されたイベントを含むウィンドウが返されるため、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

ダッシュボード

次の例は、アカウントのライフサイクル トレンドを時系列でプロットするように設計されたマルチイベント ダッシュボード検索を示しています。タンブリング ウィンドウ(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)は、各 $e1 イベントの直後に開始する 10 分間の時間枠を定義します。このウィンドウは、新しい $e1 イベントごとにスライドします。
  • 関連付け: イベントはホスト名($host)でグループ化されます。
  • 検出条件($e1 と $e2): 次の場合、特定のホストに対して検出がトリガーされます。
    • firewall_1($e1)のイベントが存在します。
    • AND、その特定の $e1 イベントから 10 分以内に、同じホストの firewall_2($e2)からの NO イベントが見つかりました。

例: 連続するイベントの欠落の検出

ルール

次の例は、プライマリ トリガーの後にセカンダリ イベントが発生しないインスタンスを特定します。このルールでは、10 分間のウィンドウ内で !$e2 条件を使用して、テレメトリーの欠落を検出します。具体的には、ファイアウォール ログが 1 つのロケーションで確認されたものの、次のホップで確認されなかった場合に、可視性のギャップやトラフィックのドロップの可能性を示すフラグが立てられます。

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
}

検索

次の例は、2 つのソース間のテレメトリーのギャップを特定するために使用される順次検索を示しています。$e1 をピボットとして使用すると、検索では、10 分以内に 2 番目のファイアウォールで対応するイベントが続かないプライマリ ファイアウォール イベントが検索されます。これは、調査中にネットワーク トラフィックの「ブラック ホール」やロギングの失敗を手動でハンティングする非常に効果的な方法です。

$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 セクション。

検索では、クエリに複数のイベントが含まれている場合、マルチイベント クエリと定義されます。ルールの場合、これを定義する方法は 2 つあります。

  • 複数のイベント(例: 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
}

検索

次の例では、マルチイベント統計検索を使用して、高頻度のログイン アクティビティを特定します。1 人のユーザーが 10 分(10m)以内に 10 回以上のログイン イベントを生成したインスタンスにフラグを付けます。

$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

例: 時間枠付きのホスト マッチング

ルール

次のルールでは、2 つのイベントを調べて $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)

コンテキストアウェア分析

YARA-L ルールを使用すると、UDM イベント データをセキュリティ侵害インジケーター(IOC)コンテキスト データに対して評価できます。このコンテキスト データは、Google SecOps のエンティティ コンテキストとして保存されます。

このセクションの例では、Google セーフ ブラウジングやカスタム フィードなどの IOC フィードとイベント フィールドを結合して、コンテキスト認識分析を作成する方法を示します。

トピック 例
セキュリティ侵害インジケーターに対するファイル ハッシュの照合 不審なプロセスの起動、セーフ ブラウジングの脅威リスト
セキュリティ侵害インジケーターに対する IP アドレスのマッチング Tor exit ノードの検出: Tor ネットワークからのログインの失敗が複数回発生した
セキュリティ侵害インジケーターに対するドメインの照合 普及率の低いドメイン
セキュリティ侵害インジケーターに対する URL の照合 脅威フィードからの悪意のある URL の一致
同時マルチフィード マッチング Tor 出口ノードとリモート アクセス ツールの検出

侵害インジケーターに対するファイル ハッシュの照合

ユースケース: 脅威フィードでセキュリティ侵害インジケーター(IOC)として特定されたファイルの実行または存在を検出します。

主なロジック: エンティティ グラフで、UDM プロセスまたはファイル イベントを IOC フィードのファイル エンティティ(ハッシュ)と結合します。

例: 不審なプロセスの起動

ルール

次のルールは、一般的な IOC コンテキスト データに対して UDM プロセスデータを評価します。プロセス起動イベントを検索し、イベントのファイル ハッシュを IOC エンティティ グラフで見つかったファイル ハッシュと結合します。

rule ProcessLaunch {
  meta:
  events:
    $ioc.graph.metadata.vendor_name = "ACME"
    $ioc.graph.metadata.product_name = "IOCs"
    $ioc.graph.metadata.entity_type = "FILE"
    $ioc.graph.entity.file.sha256 = $hash

    $process.metadata.event_type = "PROCESS_LAUNCH"
    $process.principal.hostname = $hostname
    (
        not $process.target.process.file.sha256 = "" and
        $process.target.process.file.sha256 = $hash
    )

  match:
    $hash over 15m

  condition:
    $ioc and $process
}

例: セーフ ブラウジングの脅威リスト

ルール

Google SecOps は、ファイル ハッシュに関連する脅威リストからデータを取り込み、エンティティとして保存します。次のルールは、Google セーフ ブラウジングが有害と判断したファイルの実行を検出します。

rule safe_browsing_file_execution {
    meta:
        author = "Google Security Operations"
        description = "Example usage of Safe Browsing data, to detect execution of a file that's been deemed malicious"
        severity = "LOW"

    events:
        // find a process launch event, match on hostname
        $execution.metadata.event_type = "PROCESS_LAUNCH"
        $execution.principal.hostname = $hostname

        // join execution event with Safe Browsing graph
        $sb.graph.entity.file.sha256 = $execution.target.process.file.sha256

        // look for files deemed malicious
        $sb.graph.metadata.entity_type = "FILE"
        $sb.graph.metadata.threat_intel.severity = "SEVERITY_HIGH"
        $sb.graph.metadata.product_name = "Google Safe Browsing"
        $sb.graph.metadata.source_type = "GLOBAL_CONTEXT"

    match:
        $hostname over 1h

    outcome:
        $risk_score = max(if($sb.graph.metadata.threat_intel.severity = "SEVERITY_HIGH", 90, 0))

    condition:
        $execution and $sb
}

セキュリティ侵害インジケーターに対する IP アドレスの照合

ユースケース: 脅威フィードに保存されている既知の悪意のある IP アドレスとのネットワーク通信を検出します。

主なロジック: UDM ネットワーク イベントを、エンティティ グラフの IOC フィードの IP アドレス エンティティと結合します。

例: Tor 出口ノードの検出

ルール

既知の Tor 出口ノードから発信された、または既知の Tor 出口ノードに接続されたネットワーク トラフィックを特定します。この例では、UDM イベントを GCTI フィードの Tor 出口ノード フィードと結合します。このフィードはすべてのお客様が利用できます。

rule TorExitNodeMatch {
  meta:
    author = "Google Security Operations"
    description = "Detects traffic from known Tor exit nodes."
    severity = "MEDIUM"

  events:
    // Event based on IP
    $principal_ip = $e.principal.ip

    // Filter Entity Graph to extract feed specific details
    $gcti.graph.metadata.entity_type = "IP_ADDRESS"
    $gcti.graph.metadata.feed = "Tor Exit Nodes"
    $gcti.graph.metadata.product_name = "GCTI Feed"

    // Join the IP data
    $principal_ip = $gcti.graph.entity.ip

  match:
    $principal_ip over 30m

  outcome:
    $threat_feed = array_distinct($gcti.graph.metadata.feed)

  condition:
    $e and $gcti
}

例: Tor ネットワークからのログイン失敗が複数回発生した場合

ルール

Tor ネットワークからの複数のログイン試行の失敗を検出します。この例では、login_failure イベントを Tor Exit Nodes フィードに関連付け、カウント構文(#)を使用して頻度しきい値を適用します。

rule TorMultiLoginFailure {
  meta:
    author = "Google Security Operations"
    description = "Detects multiple failed logins from a Tor network."
    severity = "HIGH"

  events:
    $failed_login_event.metadata.product_name = "login"
    $failed_login_event.metadata.product_event_type = "login_failure"
    $failed_login_event.metadata.vendor_name = "Google Workspace"
    $sus_ip = $failed_login_event.principal.ip
    $sus_target_account = $failed_login_event.target.user.email_addresses
    $sus_target_account != ""

    $gcti.graph.metadata.entity_type = "IP_ADDRESS"
    $gcti.graph.metadata.feed = "Tor Exit Nodes"
    $gcti.graph.metadata.product_name = "GCTI Feed"

    // Join the IP data
    $sus_ip = $gcti.graph.entity.ip

  match:
    $sus_ip over 1h

  outcome:
    $event_count = count_distinct($failed_login_event.metadata.id)

  condition:
    $failed_login_event and #failed_login_event >= 10 and $gcti
}

セキュリティ侵害インジケーターに対するドメインの照合

ユースケース: セキュリティ侵害インジケーター(IOC)としてフラグが設定されたドメイン、または普及率の低いドメインへのネットワーク アクティビティを検出します。

主なロジック: ドメイン名またはホスト名を使用して、UDM ネットワーク イベントを、IOC フィードのドメイン エンティティまたはエンティティ グラフの普及率データと結合します。

例: 普及率の低いドメイン

ルール

次のルールでは、普及率の低いドメインと既知の IOC を比較して検出アラートを生成します。DNS クエリイベントを普及率データと ET_PRO_IOC フィードの IOC エンティティと結合します。

rule network_prevalence_uncommon_domain_ioc_match {

  meta:
    author = "Google Security Operations"
    description = "Lookup Network DNS queries against Entity Graph for low prevalence domains with a matching IOC entry."
    severity = "MEDIUM"

  events:
    $e.metadata.event_type = "NETWORK_DNS"
    $e.network.dns.questions.name = $hostname

    //only match FQDNs, such as: exclude chrome dns access tests and other internal hosts
    $e.network.dns.questions.name = /(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/

    //prevalence entity graph lookup
    $p.graph.metadata.entity_type = "DOMAIN_NAME"
    $p.graph.entity.domain.prevalence.rolling_max > 0
    $p.graph.entity.domain.prevalence.rolling_max <= 3
    $p.graph.entity.domain.name = $hostname

    //ioc entity graph lookup
    $i.graph.metadata.vendor_name = "ET_PRO_IOC"
    $i.graph.metadata.entity_type = "DOMAIN_NAME"
    $i.graph.entity.hostname = $hostname

  match:
    $hostname over 10m

  outcome:
    $risk_score = max(
        //increment risk score based upon rolling_max prevalence
        if ( $p.graph.entity.domain.prevalence.rolling_max = 3, 50) +
        if ( $p.graph.entity.domain.prevalence.rolling_max = 2, 70) +
        if ( $p.graph.entity.domain.prevalence.rolling_max = 1, 90)
    )

  condition:
    $e and $p and $i
}

セキュリティ侵害インジケーターに対する URL の照合

ユースケース: 脅威フィードに保存されている悪意のある URL を含むネットワーク アクティビティを検出します。

主なロジック: URL データを含む UDM ネットワーク イベントを、エンティティ グラフの IOC フィードの URL エンティティと結合します。

例: 脅威フィードからの悪意のある URL の一致

ルール

次のルールは、リクエストされた URL が脅威インテリジェンス フィードの既知の悪意のある URL と一致するネットワーク イベントを検出します。イベントの target.url をエンティティ グラフ内の URL エンティティと関連付けます。

rule ioc_url_threat_feed_match {
  meta:
    author = "Google Security Operations"
    description = "Matches network events against known malicious URLs from threat intelligence feeds."
    severity = "HIGH"

  events:
    // Filter for network traffic containing URL information
    $network_event.metadata.event_type = "NETWORK_HTTP" or $network_event.metadata.event_type = "NETWORK_CONNECTION"
    $url = $network_event.target.url

    // Entity Graph Join: URL Indicators
    $ioc.graph.metadata.entity_type = "URL"
    $ioc.graph.metadata.product_name = "GCTI Feed"
    $ioc.graph.metadata.feed = "Malicious URLs"
    $ioc.graph.entity.url = $url

  match:
    $url over 5m

  outcome:
    $risk_score = 85
    $threat_feed = array_distinct($ioc.graph.metadata.feed)
    $principal_ip = array_distinct($network_event.principal.ip)
    $principal_user = array_distinct($network_event.principal.user.userid)

  condition:
    $network_event and $ioc
}

マルチフィードの同時照合

ユースケース: 1 つのイベントクエリ内で、複数の属性(IP アドレスやファイル ハッシュなど)を個別の脅威フィードと照合して、高度な脅威を関連付けます。

主要なロジック: 1 つの UDM イベントを複数のエンティティ グラフ プレースホルダと結合し、それぞれが特定の threat_feed_name( Google Cloud 脅威インテリジェンス フィードなど)をフィルタします。

例: Tor 出口ノードとリモート アクセス ツールの検出

ルール

次のルールは、リモート モニタリングと管理(RMM)に関連付けられているように見え、Tor 出口ノードから発信されるか、Tor 出口ノードに接続されるネットワーク トラフィックにフラグを設定します。NETWORK_CONNECTION イベントを IP フィード(Tor Exit Nodes)とファイルハッシュ フィード(Remote Access Tools)の両方に結合します。

rule tor_and_remote_access_tool_match {
  meta:
    author = "Google Security Operations"
    description = "Matches network connections involving both a Tor exit node and Remote Access Tool malware hashes."
    severity = "CRITICAL"

  events:
    // Filter for network connection events, often recorded by EDR agents
    $e.metadata.event_type = "NETWORK_CONNECTION"
    $target_ip = $e.target.ip
    $process_hash = $e.principal.process.file.sha256

    // IP Feed Join: Tor Exit Nodes
    $gcti_ip.graph.metadata.entity_type = "IP_ADDRESS"
    $gcti_ip.graph.metadata.product_name = "GCTI Feed"
    $gcti_ip.graph.metadata.feed = "Tor Exit Nodes"
    $gcti_ip.graph.entity.ip = $target_ip

    // Hash Feed Join: Remote Access Tools
    $gcti_hash.graph.metadata.entity_type = "FILE"
    $gcti_hash.graph.metadata.product_name = "GCTI Feed"
    $gcti_hash.graph.metadata.feed = "Remote Access Tools"
    $gcti_hash.graph.entity.file.sha256 = $process_hash

  match:
    $target_ip over 10m

  outcome:
    $risk_score = 95
    $ip_feed = array_distinct($gcti_ip.graph.metadata.feed)
    $hash_feed = array_distinct($gcti_hash.graph.metadata.feed)

  condition:
    $e and $gcti_ip and $gcti_hash
}

複合検出

複合検出は、複合ルールを使用して脅威検出を強化します。これらの複合ルールは、他のルールからの検出を入力として使用します。これにより、個々のルールでは検出できない複雑な脅威を検出できます。詳細については、複合検出の概要をご覧ください。

トピック 例
高リスクのフィルタリング 管理者権限を持つユーザーの検出
集計としきい値の設定 リスクの集計
戦術の集計 MITRE 戦術の集計
シーケンシャル複合検出 ブルート フォース攻撃の試行後にログインが成功
コンテキストアウェア検出 脅威インテリジェンスの拡充
共起検出 権限昇格とデータ漏洩の同時発生

高リスクのフィルタリング

ユースケース: 管理アカウントに関連するアクティビティなど、リスクの高い属性の既存の検出をフィルタします。

キーロジック: 既存の検出結果内の結果またはメタデータ フィールドに対して動作します。

高リスク フィルタリング複合検出は、検出結果内のフィールド(結果変数やルール メタデータなど)で動作する最もシンプルな複合検出です。管理ユーザーや本番環境など、リスクが高い可能性のある条件で検出をフィルタリングするのに役立ちます。

例: 管理者権限を持つユーザーの検出

ルール

次の複合ルールは、アクターが管理者権限を持つユーザーとして識別された既存の検出を検索し、標準化されたリスク スコアリングを適用します。

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
}

検索

次の統計検索では、特権アカウントのアクティビティを特定して集計します。「admin」ユーザーまたは「root」ユーザーに関連する検出をトリガーした一意のルール名をすべて検索するように設計されています。

この特定のクエリでは、選択した対象の期間内のすべての検出で単一の統計分析を実行するために、期間が削除されています。また、これは既存の検出データに焦点を当てた集計されていない検索であるため、[イベント] セクション、イベント変数、[条件] セクションは必要ありません。

$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 変数の両方が格納されます。また、各検出には一致変数の値が 1 つだけ含まれており、すでにキャプチャされているため、$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 時間にわたるユーザーの合計リスクを計算します。各ウィンドウのプリンシパル ユーザーごとに 1 行が出力され、複数の検出タイプにわたるアカウント リスクの概要が提供されます。

このバリエーションでは、イベント変数は必須ではありません。ルールエンジンはプリンシパル ユーザーのない検出を自動的に除外しますが、この検索では、結果にデータが入力されたものだけが含まれるように、明示的なフィルタ($principal_user != "")が必要です。デフォルトでは、クエリは特定のユーザーに対して 1 つ以上の検出が存在する場合にのみ結果を返します。

$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

ダッシュボード

このバリエーションは、ユーザーのリスクと検出アクティビティの推移をプロットするために、ダッシュボード専用に設計されています。データを個別のバケットに集計するため、トリガーされた一意のルールの数やユーザーごとの検出数の合計などの傾向を可視化するのに最適です。

このクエリでは、ウィンドウがスライディング(ホップ)ウィンドウからタンブリング ウィンドウ(by 48h)に切り替えられます。これにより、データポイントが重複しない時間セグメントにマッピングされ、時系列グラフのビジュアリゼーションがより明確になります。他の集計されていない検索と同様に、イベント変数は必要ありません。outcome セクションは、ルール名と検出 ID の両方の個別カウントを含むように拡張されています。

$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 }

検索

次の例は、既存の検出を関連付け、動的なリスク重み付けを適用する必要があるセキュリティ開発者向けに設計された検索バリアントを示しています。このクエリロジックは、detection データソースから MITRE ATT&CK の戦術とユーザー情報を抽出し、アクティビティをプリンシパル ユーザーごとにグループ化して、観測された戦術の多様性に基づいてカスタム リスク スコアリングを計算します。

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 アドレスから一連のブルート フォース攻撃アラートが発生した後にのみ発生する、アカウントへのログインの成功を検出します。

主なロジック: 共通の変数($bruteforce_ip など)で結合し、タイムスタンプの比較を使用してイベントが正しい順序で発生したことを確認することで、以前の検出と後続の未加工の UDM イベントを関連付けます。

シーケンシャル複合検出は、検出のシーケンスが重要な関連イベントのパターンを識別します。たとえば、ブルート フォース ログイン試行の検出、その後のログインの成功などです。これらのパターンには、複数のベース検出またはベース検出とイベントの組み合わせが含まれる場合があります。

例: ブルート フォース試行の後にログインが成功した場合

ルール

次の複合ルールは、シーケンスが重要な関連イベントのパターンを識別します。具体的には、Google Workspace のブルートフォース検出に続いて、24 時間以内に同じ送信元 IP からログインに成功したイベントを探します。

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 出口ノードの脅威フィードにもリストされているかどうかを確認します。

キーロジック: 複合ルールを使用して、IP アドレスなどの共有属性を照合することで、検出結果を GLOBAL_CONTEXT グラフデータ( Google Cloud 脅威インテリジェンス フィードなど)と結合します。

コンテキストアウェアの複合検出では、脅威フィードで見つかった 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.feed = "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.feed = "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 時間以内に権限昇格の検出とデータ漏洩の検出の両方をトリガーしたユーザーを特定します。

キーロジック: 集計形式を使用して、match セクション内の共有エンティティ変数($pe_user など)で結合することで、複数の異なる検出タイプを関連付けます。

同時発生複合検出は、関連するイベントの組み合わせ(ユーザーによってトリガーされた権限昇格とデータ漏洩の検出の組み合わせなど)を検出できる集計の一種です。

例: 権限昇格とデータ漏洩の同時発生

ルール

次の複合ルールは、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 セクションを含むクエリ

YARA-L 2.0 ルールに省略可能な outcome セクションを追加して、各検出の追加情報を抽出できます。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

結果を含む単一イベント クエリ

ユースケース: ユーザーリストやファイル属性に基づいて重大度タグを割り当てるなど、時間枠やイベントの関連付けを必要とせずに、リアルタイムのコンテキストで特定の時点の検出を強化します。

キーロジック: match セクションのないルールで outcome セクションを使用します。これにより、条件を満たす個々のイベントごとにメタデータを抽出し、条件付きロジック(参照リストに対するユーザーの照合など)を実行できます。

例: 特定時点の重大度タグ付け

ルール

次の例は、単一イベント ルールで 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 バイトなど)を超える脅威を特定できます。

主なロジック: outcome セクションの sum() 集計関数を使用して、match ウィンドウ内のすべてのイベントで sent_bytes と received_bytes を結合します。ルールの場合、クエリは 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)のアセット コンテキストを関連付ける検索バリアントを示しています。5 分間の match ウィンドウを使用してホスト名別にネットワーク トラフィックを集計し、データ量と脆弱性の重大度に基づいてリスク スコアリングを計算し、条件付きフィルタを適用して特定のアセット ID を最終結果セットから除外します。

$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 セクションと、outcome セクションの集計関数(max()、sum()、count() など)を削除します。この移行により、ルールはイベントを時間とともにグループ化するのではなく、イベントが到着するたびに個別に評価するようになります。match セクション)、マルチイベント ルール(match セクションを含むルール)。

outcome セクションは、単一イベントルール(一致セクションのないルール)とマルチイベント ルール(一致セクションのルール)の両方に使用できます。結果セクションを使用できるようにマルチイベントとして以前に設計したルールがある場合は、必要に応じてそれらのルールをリファクタリングして match セクションを削除して、パフォーマンスを改善できます。ルールにグループ化を適用する match セクションがなくなったため、より多くの検出項目が表示されることがありますので注意してください。

例: 結果のリファクタリング(リファクタリング前)

ルール

次の例は、イベント変数を 1 つだけ使用するマルチイベントの結果ルールを示しています。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 セクションを削除し、outcome 変数割り当てから aggregate 関数(max() など)を削除します。if ステートメント内のロジックは同じですが、グループではなく単一のイベントに適用されるようになりました。

match セクションを削除することで、クエリをリファクタリングできます。注: クエリは単一イベントになるため、outcome セクションの集計も削除する必要があります。集計の詳細については、結果の集計をご覧ください。

例: Outcome 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() の結果をプレースホルダ変数に割り当てます。

例: 関数からプレースホルダ変数への割り当て

プレースホルダ変数は関数呼び出しの結果に割り当てることができます。また、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

ダッシュボード

次の例は、時系列の可視化に最適化されたダッシュボードのバリエーションを示しています。このクエリでは、分単位の粒度ではなく 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

最適化とフィルタリング

効果的なルール最適化は、正確なデータフィルタリングに依存しています。これにより、検出エンジンは意味のある情報のみを処理します。「ノイズ」の多いデータや不完全なデータを除外することで、ルールのパフォーマンスを大幅に向上させ、生成されたアラートが実用的なものになるようにすることができます。

トピック 例
ゼロ値の除外 明示的および暗黙的なゼロ値の除外

ゼロ値の除外

ユースケース: 実行可能なセキュリティ データを提供しない空の文字列、null 値、汎用のプレースホルダ アカウント(「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
}

検索

match セクションのプレースホルダには暗黙的なゼロ値フィルタがないため、hostname を空の文字列にできないことを明示的に指定する必要があります。

$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

ダッシュボード

match セクションのプレースホルダには暗黙的なゼロ値フィルタがないため、hostname を空の文字列にできないことを明示的に記述する必要があります。

$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 のプロフェッショナルから回答を得ることができます。