YARA-L 2.0 言語の概要
YARA-L 2.0 は、エンタープライズ ログデータを Google Security Operations インスタンスに取り込んで検索する際のルールの作成に使用されるコンピュータ言語です。YARA-L 構文は、VirusTotal が開発した YARA 言語から派生しています。 この言語は Google Security Operations Detection Engine と組み合わせて動作し、大量のデータ全体で脅威などのイベントを検出できます。
詳しくは以下をご覧ください。
YARA-L 2.0 のサンプルルール
次の例は、YARA-L 2.0 で書かれたルールを示しています。それぞれは、ルール言語内でイベントを相関させる方法を示しています。
ルールとチューニング
次のルールは、イベントデータの特定のパターンをチェックし、パターンが見つかったら検出を作成します。このルールには、イベントタイプを追跡するための変数 $e1
と metadata.event_type
UDM フィールドが含まれます。このルールは、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
}
異なる都市からのログイン
次のルールは、2 つ以上の都市から 5 分以内に企業にログインしたユーザーを検索します。
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
Event 変数:$udm
プレースホルダ変数: $city
と $user
このルールの仕組みは次のとおりです。
- ユーザー名(
$user
)を持つイベントをグループ化し、一致するものが見つかったらそれ($user
)を返します。 - 期間は 5 分です。これは、5 分未満の間隔のイベントどうしのみが関連付けられることを意味します。
- イベントタイプが USER_LOGIN のイベント グループ(
$udm
)を検索します。 - そのイベントグループについて、ルールはユーザー ID を
$user
として、ログインの都市を$city.
として呼び出します。 - 5 分間以内にイベント グループ(
$udm
)でcity
値の固有の数(#city
で示される)が 1 より大きい場合に一致を返します。
迅速なユーザーの作成と削除
次のルールは、4 時間以内に作成され、削除されたユーザーを検索します。
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
}
Event 変数:$create
および $delete
変数に一致: $user
プレースホルダ変数: 該当なし
このルールの仕組みは次のとおりです。
- ユーザー名(
$user
)を持つイベントをグループ化し、一致するものが見つかったらそれ($user
)を返します。 - 時間枠は 4 時間です。これは、4 時間未満のイベントのみが関連付けられることを意味します。
- 2 つのイベント グループを検索します(
$create
と$delete
。$create
は#create >= 1
と同等)。 $create
はUSER_CREATION
イベントに対応し、ユーザー ID を$user
として呼び出します。$user
は 2 つのイベント グループを結合するために使用されます。$delete
はUSER_DELETION
イベントに対応し、ユーザー ID を$user
として呼び出します。このルールは、2 つのイベント グループのユーザー ID が同じ一致を検索します。- このルールは、
$delete
のイベントが$create
のイベントより後で発生した場合を検索し、見つかったときに一致を返します。
単一イベントルール
単一イベントルールは、1 つのイベントに関連するルールです。単一イベントルールを次のように指定できます。
- 一致セクションがないルール。
match
セクションとcondition
セクションで、1 つのイベントが存在するかどうかのみを確認するルール(例: 「$e」、「#e > 0」、「#e >= 1」、「1 <= #e」、「0 < #e」)。
たとえば、次のルールはユーザーのログイン イベントを検索し、Google セキュリティ オペレーション アカウント内に保存された企業データ内で最初に検出されたものを返します。
rule SingleEventRule {
meta:
author = "noone@altostrat.com"
events:
$e.metadata.event_type = "USER_LOGIN"
condition:
$e
}
一致セクションを含む単一イベントルールの別の例を以下に示します。このルールは、5 分未満に 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
}
rule MultiEventRule{
meta:
author = "alice@example.com"
description = "Rule with outcome condition and simple existence condition on one event variable"
events:
$e.metadata.event_type = "USER_LOGIN"
$e.principal.user.userid = $user
match:
$user over 10m
outcome:
$num_events_in_match_window = count($e.metadata.id)
condition:
#e > 0 and $num_events_in_match_window >= 10 // Could be rewritten as #e >= 10
}
マルチイベントのルール
マルチイベントルールを使用して、特定の期間に多くのイベントをグループ化し、イベント間の相関関係の特定を試みます。一般的なマルチイベントルールは次のようになります。
- イベントをグループ化する期間を指定する
match
セクション。 - 検出をトリガーし、複数のイベントの存在を確認する条件を指定する
condition
セクション。
たとえば、次のルールは、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
}
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
}
any と all のルールの例
次のルールは、すべての発信元 IP アドレスが 5 分以内に安全であるとわかっている 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
}
ルール内の正規表現
次の YARA-L 2.0 正規表現の例では、match.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
}
スライディング ウィンドウのルールの例
次の YARA-L 2.0 スライディング ウィンドウの例では、firewall_1
イベントの後で firewall_2
イベントが欠落していることを検索します。after
キーワードは、ピボット イベント変数 $e1
と併用され、イベントを関連付ける際にチェックを行う必要がある時間枠として、各 firewall_1
イベントの後に 10 分間のみの時間枠を指定します。
rule SlidingWindowRuleExample {
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:
$host over 10m after $e1
condition:
$e1 and !$e2
}
ゼロ値の除外の例
ルールエンジンは、match
セクションで使用されているすべてのプレースホルダのゼロ値を暗黙的に除外します。詳細については、match
セクションのゼロ値の処理をご覧ください。これを無効にするには、allow_zero_values で説明されている 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
}
outcome
セクションを含むルールの例
YARA-L 2.0 ルールにオプションの outcome
セクションを追加して、各検出の追加情報を抽出できます。条件セクションでは、結果の変数に条件を指定することもできます。検出ルールの outcome
セクションを使用して、ダウンストリームでの消費のための変数を設定できます。たとえば、分析するイベントのデータに基づいて重大度スコアを設定できます。
詳しくは以下をご覧ください。
結果セクションを含むマルチイベントルール:
次のルールでは、$hostname
の値を取得するために 2 つのイベントを調べます。$hostname
の値が 5 分間を超えて一致した場合、重大度スコアが適用されます。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")
}
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")
}
結果セクションを含む単一イベントルール:
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.userid in %admin_users, "SEVERE", "MODERATE")
$severity_tag = if($file_size > 1024, $admin_severity, "LOW")
condition:
$u
}
マルチイベントの結果ルールを単一イベントの結果ルールにリファクタリングする。
outcome
セクションは、単一イベントルール(match
セクションのないルール)とマルチイベント ルール(match
セクションを含むルール)の両方に使用できます。結果セクションを使用できるようにするために、以前にマルチイベントになるようにルールを設計した場合は、必要に応じて match
セクションを削除してルールをリファクタリングし、パフォーマンスを改善できます。ルールにグループ化を適用する match
セクションがなくなったため、より多くの検出項目が表示されることがありますので注意してください。このリファクタリングは、次の例に示すように、1 つのイベント変数を使用するルールでのみ実施できます。
イベント変数を 1 つだけ使用するマルチイベント結果ルール(リファクタリングの候補)。
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
}
match
セクションを削除することで、ルールをリファクタリングできます。また、ルールは単一イベントになるため、outcome
セクションの集計も削除する必要があります。集計の詳細については、結果の集計をご覧ください。
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
}
関数からプレースホルダ ルールへの変換の例
プレースホルダ変数は関数呼び出しの結果に割り当てることができます。また、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.from , "(.*)@")
// Use function-placeholder assignment to normalize an email:
// uid@??? -> uid@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, 10, 0) +
if($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
}
結果条件ルールの例
condition
セクションでは、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
}