YARA-L 2.0 言語の概要

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

YARA-L 2.0 は、エンタープライズ ログデータを Google Security Operations インスタンスに取り込んで検索する際のルールの作成に使用されるコンピュータ言語です。YARA-L 構文は、VirusTotal が開発した YARA 言語から派生しています。 この言語は Google SecOps Detection Engine と組み合わせて動作し、大量のデータ全体で脅威などのイベントを検出できます。

詳しくは以下をご覧ください。

YARA-L 2.0 のルールの例

次の例は、YARA-L 2.0 で書かれたルールを示しています。それぞれは、ルール言語内でイベントを相関させる方法を示しています。

ルールとチューニング

次のルールは、イベントデータの特定のパターンをチェックし、パターンが見つかった場合は検出を作成します。このルールには、イベントタイプと 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
}

異なる都市からのログイン

次のルールは、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 と同等)。
  • $createUSER_CREATION イベントに対応しており、$user としてユーザー ID を呼び出します。
  • $user は 2 つのイベント グループを結合するために使用されます。
  • $deleteUSER_DELETION イベントに対応しており、$user としてユーザー ID を呼び出します。このルールは、2 つのイベント グループのユーザー ID が同じ一致を検索します。
  • このルールは、$delete のイベントが $create のイベントより後で発生した場合を検索し、見つかったときに一致を返します。

単一イベントのルール

単一イベントルールは、1 つのイベントに関連するルールです。単一イベントルールを次のように指定できます。

  • 一致セクションがないルール。
  • match セクションと condition セクションで、1 つのイベントが存在するかどうかのみを確認するルール(例: 「$e」、「#e > 0」、「#e >= 1」、「1 <= #e」、「0 < #e」)。

たとえば、次のルールはユーザーのログイン イベントを検索し、Google SecOps アカウント内に保存された企業データ内で最初に検出されたものを返します。

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
}

複合ルールの例

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

Tripwire 検出

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

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.outcomes["principal_users"]
    $principal_user = /admin|root/ nocase

  match:
    $principal_user over 1h

  outcome:
    $risk_score = 75
    $upstream_rules = array_distinct($rule_name)

  condition:
    $d
}

しきい値と集計の検出

集計複合検出ルールを使用すると、ホスト名やユーザー名などの共有属性に基づいて検出結果をグループ化し、集計されたデータを分析できます。一般的なユースケースは次のとおりです。

  • セキュリティ アラートや集約されたリスクを大量に生成するユーザーを特定します。
  • 関連する検出を集約して、不審なアクティビティ パターンを持つホストを検出する。

リスク集計の例:

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)
    $principal_users = array_distinct($principal_users)
    $upstream_rules = array_distinct($rule_name)

  condition:
    $d and $cumulative_risk > 500
}

戦術の集計の例:

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.rule_labels["tactic"]
    $rule_name = $d.detection.detection.rule_name

  match:
    $principal_user over 48h

  outcome:
    $mitre_tactics_count = count_distinct($tactic)
    $mitre_tactics = array_distinct($d.detection.rule_labels["tactic"])
    $risk_score = min(100, (50+15*$mitre_tactics_count))
    $upstream_rules = array_distinct($rule_name)

  condition:
    $d and $mitre_tactics_count > 1
}

順次複合検出

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

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 = $d.detection.detection.outcomes["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
}

コンテキストアウェア検出

コンテキストアウェア複合検出では、脅威フィードで見つかった IP アドレスなどの追加のコンテキストを使用して検出を強化します。

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:
    $detection_ip = $d.detection.detection.outcomes["principal_ips"]
    $gcti.graph.metadata.entity_type = "IP_ADDRESS"
    $gcti.graph.metadata.vendor_name = "Google Cloud Threat Intelligence"
    $gcti_feed.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 = $gcti.graph.entity.ip

    $rule_name = $d.detection.detection.rule_name
    $risk = $d.detection.detection.outcomes["risk_score"]

  match:
    $detection_ip, $rule_name over 1h

  outcome:
    $risk_score = 80
    $upstream_rule = array_distinct($rule_name)

  condition:
    $d and $gcti
}

同時発生の検出

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

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"

    $pe_user = $privilege_escalation.detection.detection.outcomes["principal_users"]
    $ex_user = $exfiltration.detection.detection.outcomes["principal_users"]

    $pe_user = $ex_user

  match:
    $pe_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
}

スライディング ウィンドウのルールの例

次の 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 セクションを使用して、ダウンストリームでの消費のための変数を設定できます。たとえば、分析対象のイベントからのデータに基づいて重大度スコアを設定できます。

詳しくは以下をご覧ください。

結果セクションを含むマルチイベントルール:

次のルールでは、2 つのイベントを調べて $hostname の値を取得します。$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
}

さらにサポートが必要な場合 コミュニティ メンバーや Google SecOps のプロフェッショナルから回答を得ることができます。