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 行を出力します。クエリはウィンドウあたりのボリューム カウントに焦点を当てているため、結果には 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(): この関数は、指定された IP アドレスが、サブネット マッチング用の指定されたクラスレス ドメイン間ルーティング(CIDR)サブネットに含まれているかどうか、および文字列配列の or 演算子を確認します。- 論理演算子
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 の検証
ルール
次のルールは、すべての発信元 IP アドレスが 5 分以内(5m)に安全であるとわかっている 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 でグループ化し、個別の位置の値を検索します。
例: 複数都市でのログインの検出
ルール
次のルールは、2 つ以上の都市から 5 分(5m)以内に企業にログインしたユーザーを検索します。ここで、$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
ダッシュボード
次のサンプルクエリは、アカウントの不正使用の可能性を追跡するための同等のダッシュボードの可視化を提供します。USER_LOGIN イベントを 5 分(5m)のウィンドウでユーザー別に集計し、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)
複合検出
複合検出は、複合ルールを使用して脅威検出を強化します。これらの複合ルールは、他のルールの検出を入力として使用します。これにより、個々のルールでは検出できない複雑な脅威を検出できます。詳細については、複合検出の概要をご覧ください。
| トピック | 例 |
|---|---|
| 高リスクのフィルタリング | 管理者権限を持つユーザーの検出 |
| 集計としきい値処理 | リスクの集計 |
| 戦術の集計 | 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」ユーザーを含む検出をトリガーしたすべての一意のルール名を特定するように設計されています。
この特定のクエリでは、選択した対象の期間内のすべての検出で 1 回の統計分析を実行するために、時間枠が削除されています。また、これは既存の検出データに焦点を当てた集計されていない検索であるため、イベント セクション、イベント変数、条件セクションは必要ありません。
$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 イベントを関連付けます。
シーケンシャル複合検出は、ブルート フォース ログイン試行の検出に続いてログインが成功するなど、検出の順序が重要な関連イベントのパターンを識別します。これらのパターンには、複数のベース検出またはベース検出とイベントの組み合わせが含まれる場合があります。
例: ブルート フォース試行の後にログインが成功した場合
ルール
次の複合ルールは、シーケンスが重要な関連イベントのパターンを識別します。具体的には、24 時間以内に同じ送信元 IP からのログイン成功イベントが続く Google Workspace ブルート フォース検出を探します。
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 Threat Intelligence フィードなど)と結合します。
コンテキストアウェアの複合検出では、脅威フィードで見つかった 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 時間以内に権限昇格の検出とデータ漏洩の検出の両方をトリガーしたユーザーを特定します。
キーロジック: 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 (: #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() の結果をプレースホルダ変数に割り当てます。
例: 関数からプレースホルダ変数への割り当て
プレースホルダ変数は関数呼び出しの結果に割り当てることができます。また、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 のプロフェッショナルから回答を得ることができます。