コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

YARA-L 2.0 言語の構文

このセクションでは、YARA-L 構文の主な要素について説明します。YARA-L 2.0 言語の概要もご覧ください。

ルールの構造

YARA-L 2.0 では、変数の宣言、定義、用途を次の順序で指定する必要があります。

  1. メタ
  2. イベント
  3. 一致(省略可)
  4. outcome(省略可)
  5. 条件
  6. options(省略可能)

次の図は、ルールの一般的な構造を示しています。

rule <rule Name>
{
  meta:
    // Stores arbitrary key-value pairs of rule details, such as who wrote
    // it, what it detects on, version control, etc.

  events:
    // Conditions to filter events and the relationship between events.

  match:
    // Values to return when matches are found.

  outcome:
    // Additional information extracted from each detection.

  condition:
    // Condition to check events and the variables used to find matches.

  options:
    // Options to turn on or off while executing this rule.
}

コメント

C の場合と同様、コメントは 2 つのスラッシュ文字(// comment)により指定します。コメントが複数行にわたる場合は、スラッシュ アスタリスク(/* comment */)を使用してコメントアウトします。

リテラル

負でない整数(小数点なし)、文字列、ブール値、正規表現のリテラルがサポートされています。

文字列リテラルと正規表現リテラル

YARA-L 2.0 で文字列を囲むには、次のいずれかの引用符を使用します。ただし、引用符で囲まれたテキストは、使用する引用符に応じて解釈が異なります。

  1. 二重引用符(")- 通常の文字列に使用します。エスケープ文字を含める必要があります。
    たとえば、「hello\tworld」の場合、\t はタブとして解釈されます。

  2. バッククォート(`)- すべての文字を文字どおりに解釈するために使用します。
    たとえば、「hello\tworld」の場合、\t はタブとして解釈されません。

正規表現には 2 つのオプションがあります。

re.regex() 関数を使用せずに正規表現を直接使用する場合は、正規表現リテラルに /regex/ を使用します。

re.regex() 関数を使用する場合は、文字列リテラルを正規表現リテラルとして使用することもできます。二重引用符文字列リテラルの場合は、バックスラッシュ文字でバックスラッシュ文字をエスケープする必要があります。これは、ややスマートではない処理の仕方です。

たとえば、次の 2 つの式は等価です。

  • re.regex($e.network.email.from, `.*altostrat\.com`)
  • re.regex($e.network.email.from, ".*altostrat\\.com")
  • $e.network.email.from = /.*altostrat\.com/

読みやすくするために、正規表現の文字列にはバッククォート文字を使用することをおすすめします。

演算子

YARA-L では次の演算子を使用できます。

オペレーター Description
= 等しい / 宣言
!= 等しくない
< 次より小さい
<= 以下
> 次より大きい
>= 以上

変数

YARA-L 2.0 では、すべての変数が $<variable name> として表されます。

次のタイプの変数を定義できます。

  • イベント変数 — イベントのグループを正規化された形式(UDM)またはエンティティ イベントで表します。events セクションで、イベント変数の条件を指定します。名前、イベントソース、イベント フィールドを使用して、イベント変数を指定します。許可されるソースは、udm(正規化されたイベントの場合)と graph(エンティティ イベントの場合)です。ソースを省略すると、udm がデフォルトのソースとして設定されます。イベント フィールドは、.<field name> のチェーンで表されます(例: $e.field1.field2)。イベント フィールド チェーンは常に最上位のソース(UDM またはエンティティ)から始まります。

  • 一致変数 - match セクションで宣言します。一致変数は、一意の変数セットのセット(および期間)ごとに 1 行が返されるため、クエリのグループ フィールドになります。ルールが一致を見つけると、一致変数の値が返されます。各一致変数が表す内容を events セクションで指定します。

  • プレースホルダ変数 - events セクションで宣言および定義します。プレースホルダ変数は、match 変数に似ています。ただし、condition セクションでプレースホルダ変数を使用して、一致条件を指定できます。

推移結合条件を使用して、イベント フィールド間の関係を宣言するために、一致変数とプレースホルダ変数を使用します(詳細については、events セクションの構文をご覧ください)。

マップ

構造体とラベル

一部の UDM フィールドは、構造体ラベルデータ型を使用します。

構造体とラベルの両方で特定の Key-Value ペアを検索するには、標準のマップ構文を使用します。

// A Struct field.
$e.udm.additional.fields["pod_name"] = "kube-scheduler"
// A Label field.
$e.metadata.ingestion_labels["MetadataKeyDeletion"] = "startup-script"

サポート対象のケース

イベントと結果のセクション
// Using a Struct field in the events section
events:
  $e.udm.additional.fields["pod_name"] = "kube-scheduler"

// Using a Label field in the outcome section
outcome:
  $value = array_distinct($e.metadata.ingestion_labels["MetadataKeyDeletion"])
マップ値のプレースホルダへの割り当て
$placeholder = $u1.metadata.ingestion_labels["MetadataKeyDeletion"]
結合条件でのマップ フィールドの使用
// using a Struct field in a join condition between two udm events $u1 and $u2
$u1.metadata.event_type = $u2.udm.additional.fields["pod_name"]

サポート対象外のケース

キーワード any または all とマップの組み合わせ

たとえば、現在、「all $e.udm.additional.fields["pod_name"] = "kube-scheduler"」はサポートされていません。

関数

このセクションでは、Chronicle が Detection Engine でサポートする YARA-L 2.0 関数について説明します。

これらの関数は、ルール内の次の領域で使用できます。

文字列関数

Chronicle は、次の文字列操作関数をサポートしています。

  • strings.concat(a, b)
  • strings.coalesce(a, b)
  • strings.to_lower(stringText)
  • strings.to_upper(stringText)
  • strings.base64_decode(encodedString)

以降のセクションでは、それぞれの使用方法について説明します。

文字列または整数を連結する

2 つの文字列、2 つの整数、またはそれらの組み合わせを連結した値を返します。

strings.concat(a, b)

この関数は、文字列または整数の 2 つの引数を取り、2 つの値を連結して文字列として返します。整数は連結する前に文字列にキャストされます。引数には、リテラルまたはイベント フィールドを使用できます。両方の引数がフィールドである場合、2 つの属性は同じイベントからのものでなければなりません。

次の例には、引数として文字列変数と文字列リテラルが含まれています。

"google-test" = strings.concat($e.principal.hostname, "-test")

次の例には、引数として文字列変数と整数変数が含まれています。principal.hostname と principal.port の両方は、同じイベント $e からのもので、文字列を返すために連結されます。

"google80" = strings.concat($e.principal.hostname, $e.principal.port)

次の例では、イベント $e1 の principal.port とイベント $e2 の principal.hostname を連結しようとしています。引数が異なるイベント変数であるため、コンパイラ エラーが返されます。

// returns a compiler error
"test" = strings.concat($e1.principal.port, $e2.principal.hostname)

文字列値を結合する

空の文字列と評価されない最初の式の値を返します(例: 「ゼロ以外の値」)。 両方の引数が空の文字列と評価されると、関数の呼び出しは空の文字列を返します。

strings.coalesce(a, b)

引数には、リテラル、イベント フィールド、関数呼び出しを使用できます。どの引数も、文字列型でなければなりません。両方の引数がフィールドである場合、2 つの属性は同じイベントからのものでなければなりません。

次の例には、引数として文字列変数と文字列リテラルが含まれています。 (1)$e.network.email.fromsuspicious@gmail.com であるか、(2)$e.network.email.from が空かつ$e.network.email.tosuspicious@gmail.com の場合、条件は true と評価されます。

"suspicious@gmail.com" = strings.coalesce($e.network.email.from, $e.network.email.to)

次の例には、ネストされた結合呼び出しが含まれています。この条件は、イベント $e の最初の null 以外の IP アドレスを、リファレンス リスト ip_watchlist の値と比較します。この呼び出しで引数が結合される順序は、ルール条件で引数が列挙されている順序と同じです。

  1. $e.principal.ip が最初に評価されます。
  2. $e.src.ip が次に評価されます。
  3. $e.target.ip が次に評価されます。
  4. 最後に、以前の IP フィールドが設定されていない場合、文字列「No IP」がデフォルト値として返されます。
strings.coalesce(
  strings.coalesce($e.principal.ip, $e.src.ip),
  strings.coalesce($e.target.ip, "No IP")
) in %ip_watchlist

次の例では、イベント $e1 とイベント $e2principal.hostname を結合しようとしています。引数が異なるイベント変数であるため、コンパイラ エラーが返されます。

// returns a compiler error
"test" = strings.coalesce($e1.principal.hostname, $e2.principal.hostname)

文字列を大文字または小文字に変換する

これらの関数は、すべての文字を大文字または小文字に変更した後、文字列テキストを返します。

  • strings.to_lower(stringText)
  • strings.to_upper(stringText)
"test@google.com" = strings.to_lower($e.network.email.from)
"TEST@GOOGLE.COM" = strings.to_upper($e.network.email.to)

文字列を Base64 でデコードする

エンコードされた文字列の Base64 デコードされたバージョンを含む文字列を返します。

strings.base64_decode(encodedString)

この関数は、Base64 でエンコードされた 1 つの文字列を引数として受け取ります。encodedString が Base64 でエンコードされた有効な文字列でない場合、この関数は encodedString をそのまま返します。

この例は、principal.domain.name が "dGVzdA==" であり、文字列 "test" の base64 エンコードである場合、True を返します。

"test" = strings.base64_decode($e.principal.domain.name)

正規表現関数

Chronicle は、次の正規表現関数をサポートしています。

  • re.regex(stringText, regex)
  • re.capture(stringText, regex)
  • re.replace(stringText, replaceRegex, replacementText)

正規表現の一致

YARA-L 2.0 では、次のいずれかの構文を使用して正規表現の一致を定義できます。

  • YARA 構文の使用 - イベントに関連します。この構文の一般的な表現は次のとおりです。$e.field = /regex/
  • YARA-L 構文の使用 - 次のパラメータを受け取る関数として使用します。
    • 正規表現が適用されるフィールド。
    • 文字列として指定された正規表現。文字列の後に nocase 修飾子を使用すると、検索で大文字小文字の区別が無視されるよう指定できます。この構文の一般的な表現は次のとおりです。re.regex($e.field, `regex`)

YARA-L 2.0 で正規表現を定義するときには、次の点に注意してください。

  • いずれの場合も、指定した正規表現に一致する部分文字列が文字列に含まれている場合、述語は true です。正規表現の先頭または末尾に .* を追加する必要はありません。
  • 文字列と完全に一致する場合、またはプレフィックスまたはサフィックスのみと一致する場合は、正規表現に ^(開始)と $(終了)のアンカー文字を含めます。たとえば、/^full$/"full" と正確に一致しますが、/full/"fullest""lawfull""joyfully" と一致します。
  • UDM フィールドに改行文字が含まれている場合、regexp は UDM フィールドの最初の行にのみ一致します。完全な UDM フィールド マッチングを適用するには、(?s) を正規表現に追加します。たとえば、/.*allUDM.*//(?s).*allUDM.*/ に置き換えます。

正規表現のキャプチャ

引数で指定された正規表現パターンを使用して、文字列からデータをキャプチャ(抽出)します。

re.capture(stringText, regex)

この関数は次の 2 つの引数を取ります。

  • stringText: 検索する元の文字列。
  • regex: 検索するパターンを示す正規表現。

正規表現では、0 または 1 つのキャプチャ グループをかっこで囲むことができます。正規表現にキャプチャ グループがない場合、この関数は最初に一致した部分文字列を返します。正規表現に 1 つのキャプチャ グループが含まれている場合、キャプチャ グループの最初の一致部分文字列が返されます。2 つ以上のキャプチャ グループを定義すると、コンパイラ エラーが返されます。

この例では、$e.principal.hostname に「aaa1bbaa2」が含まれていれば、この関数は最初のインスタンスを返すため、以下は True になります。この例にはキャプチャ グループはありません。

"aaa1" = re.capture($e.principal.hostname, "a+[1-9]")

この例は、メール内の @ 記号より後ろのすべての部分を取得します。$e.network.email.from フィールドが test@google.com の場合、この例では google.com を返します。この例には、キャプチャ グループが 1 つ含まれています。

"google.com" = re.capture($e.network.email.from , "@(.*)")

正規表現がテキスト内の部分文字列と一致しない場合、この関数は空の文字列を返します。空の文字列を除外することで、一致しないイベントを省略できます。これは、re.capture() が不等式を使用する場合に特に重要です。

// Exclude the empty string to omit events where no match occurs.
"" != re.capture($e.network.email.from , "@(.*)")

// Exclude a specific string with an inequality.
"google.com" != re.capture($e.network.email.from , "@(.*)")

正規表現の置き換え

正規表現の置き換えを行います。

re.replace(stringText, replaceRegex, replacementText)

この関数は次の 3 つの引数を取ります。

  • stringText: 元の文字列。
  • replaceRegex: 検索するパターンを示す正規表現。
  • replacementText: 各一致に挿入するテキスト。

元の stringText から派生した新しい文字列を返します。replaceRegex のパターンと一致するすべての部分文字列が replacementText の値に置き換えられます。replacementText で、バックスラッシュでエスケープされた数字(\1 ~\9)を使用して、対応する括弧で囲まれたグループと一致するテキストを置き換えます。一致するテキスト全体を参照するには、\0 を使用します。

この関数は、重複しない一致を置き換えるもので、最初に見つかったものを優先的に置き換えます。たとえば、re.replace("banana", "ana", "111") は、文字列「b111na」を返します。

この例では、メール内の @ 記号の後のすべての部分をキャプチャし、comorg に置き換えて結果を返します。ネストされた関数が使用されることに注意してください。

"email@google.org" = re.replace($e.network.email.from, "com", "org")

次の例では、replacementText 引数でバックスラッシュでエスケープされた数字を使用して、replaceRegex パターンとの一致を参照します。

"test1.com.google" = re.replace(
                       $e.principal.hostname, // holds "test1.test2.google.com"
                       "test2\.([a-z]*)\.([a-z]*)",
                       "\\2.\\1"  // \\1 holds "google", \\2 holds "com"
                     )

空の文字列と re.replace() を処理する場合は、次の点に注意してください。

replaceRegex として空の文字列を使用。

// In the function call below, if $e.principal.hostname contains "name",
// the result is: 1n1a1m1e1, because an empty string is found next to
// every character in `stringText`.
re.replace($e.principal.hostname, "", "1")

空の文字列を置き換えるには、"^$"replaceRegex として使用します。

// In the function call below, if $e.principal.hostname contains the empty
// string, "", the result is: "none".
re.replace($e.principal.hostname, "^$", "none")

日付関数

Chronicle は、以下の日付関連の関数をサポートしています。

  • timestamp.get_minute(unix_seconds [, time_zone])
  • timestamp.get_hour(unix_seconds [, time_zone])
  • timestamp.get_day_of_week(unix_seconds [, time_zone])
  • timestamp.get_week(unix_seconds [, time_zone])
  • timestamp.current_seconds()

Chronicle は、unix_seconds 引数として負の整数をサポートしています。負の整数は、Unix エポックより前の時刻を表します。無効な整数(オーバーフローする値など)を指定すると、-1 が返されます。これは珍しいシナリオです。

YARA-L 2 では負の整数リテラルがサポートされていないため、不等価演算子を使用してこの条件を確認してください。例:

0 > timestamp.get_hour(123)

時間抽出

[0, 59] の範囲の整数を返します。

timestamp.get_minute(unix_seconds [, time_zone])

次の関数は、時刻を表す [0, 23] の範囲の整数を返します。

timestamp.get_hour(unix_seconds [, time_zone])

次の関数は、日曜日から始まる曜日を表す [1, 7] の範囲の整数を返します。たとえば、1 = 日曜日、2 = 月曜日などです。

timestamp.get_day_of_week(unix_seconds [, time_zone])

次の関数は、1 年の中の週を表す [0, 53] の範囲の整数を返します。週は日曜日から始まります。年の最初の日曜日より前の日付は 0 週目です。

timestamp.get_week(unix_seconds [, time_zone])

これらの時間抽出関数には同じ引数があります。

  • unix_seconds は、$e.metadata.event_timestamp.seconds などの Unix エポックからの経過秒数を表す整数か、その値を含むプレースホルダです。
  • time_zone は省略可能で、time_zone を表す文字列です。省略した場合、デフォルトは「GMT」です。文字列リテラルを使用してタイムゾーンを指定できます。次のオプションがあります。
    • TZ データベース名(「America/Los_Angeles」など)。詳しくは、このページの「TZ データベース名」列をご覧ください。
    • (+|-)H[H][:M[M]] 形式の UTC からのタイムゾーン オフセット(例: 「-08:00」)。

この例では、time_zone 引数が省略されているため、デフォルトの「GMT」になります。

$ts = $e.metadata.collected_timestamp.seconds

timestamp.get_hour($ts) = 15

次の例では、文字列リテラルを使用して time_zone を定義しています。

$ts = $e.metadata.collected_timestamp.seconds

2 = timestamp.get_day_of_week($ts, "America/Los_Angeles")

以下に、その他の有効な time_zone 指定子の例を示します。これらは、時間抽出関数に 2 番目の引数として渡すことができます。

  • "America/Los_Angeles" または "-08:00""PST" はサポートされていません)
  • "America/New_York" または "-05:00""EST" はサポートされていません)
  • "Europe/London"
  • "UTC"
  • "GMT"

現在のタイムスタンプ

現在の時刻を Unix 秒数で表す整数を返します。これは検出のタイムスタンプとほぼ同じで、ルールが実行されるタイミングに基づきます。

timestamp.current_seconds()

次の例は、証明書が 24 時間を超えて期限切れになった場合に True を返します。現在の Unix 秒を減算して、不等価演算子を使用して比較することで、時間差を計算します。

86400 < timestamp.current_seconds() - $e.network.tls.certificate.not_after

数学関数

絶対値。

整数式の絶対値を返します。

math.abs(intExpression)

この例では、イベントが指定された時刻の前か後かに関係なく、指定された時刻(Unix エポックからの秒数)から 5 分以上経過した場合に True を返します。math.abs の呼び出しは、複数の変数またはプレースホルダに依存できません。たとえば、以下の例の 1643687343 のハードコードされた時間値を $e2.metadata.event_timestamp.seconds に置き換えることはできません。

300 < math.abs($e1.metadata.event_timestamp.seconds - 1643687343)

Net 関数

指定された IP アドレスが指定されたサブネットワーク内にある場合に true を返します。

net.ip_in_range_cidr(ipAddress, subnetworkRange)

YARA-L を使用すると、net.ip_in_range_cidr() ステートメントを使用するサブネットワーク内のすべての IP アドレスにわたり UDM イベントを検索できます。IPv4 と IPv6 の両方がサポートされています。

IP アドレスの範囲を検索するには、IP UDM フィールドとクラスレス ドメイン間ルーティング(CIDR)範囲を指定します。YARA-L は、単一および繰り返しの IP アドレス フィールドの両方を処理できます。

IPv4 の例:

net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/24")

IPv6 の例:

net.ip_in_range_cidr($e.network.dhcp.yiaddr, "2001:db8::/32")

net.ip_in_range_cidr() ステートメントを使用したルールの例については、サンプルのルールとして IP アドレスの範囲内の単一イベントをご覧ください。

プレースホルダの割り当てに対する関数

関数の呼び出し結果は、events セクションのプレースホルダに割り当てることができます。例:

$placeholder = strings.concat($e.principal.hostname, "my-string").

その後、matchconditionoutcome セクションでプレースホルダ変数を使用できます。 ただし、プレースホルダの割り当てには 2 つの制限があります。

  1. プレースホルダの割り当てに対応するすべてのプレースホルダを、イベント フィールドを含む式に割り当てる必要があります。たとえば、次の例は有効です。

    $ph1 = $e.principal.hostname
    $ph2 = $e.src.hostname
    
    // Both $ph1 and $ph2 have been assigned to an expression containing an event field.
    $ph1 = strings.concat($ph2, ".com")
    
    $ph1 = $e.network.email.from
    $ph2 = strings.concat($e.principal.hostname, "@gmail.com")
    
    // Both $ph1 and $ph2 have been assigned to an expression containing an event field.
    $ph1 = strings.to_lower($ph2)
    

    ただし、次の例は無効です。

    $ph1 = strings.concat($e.principal.hostname, "foo")
    $ph2 = strings.concat($ph1, "bar") // $ph2 has NOT been assigned to an expression containing an event field.
    
  2. 関数呼び出しは、いずれか 1 つのイベントに依存する必要があります。ただし、同じイベントからの複数のフィールドが関数呼び出し引数で使用される場合があります。 たとえば、以下は有効です。

    $ph = strings.concat($event.principal.hostname, "string2")

    $ph = strings.concat($event.principal.hostname, $event.src.hostname)

    ただし、以下は無効です。

    $ph = strings.concat("string1", "string2")

    $ph = strings.concat($event.principal.hostname, $anotherEvent.src.hostname)

リファレンス リストの構文

リファレンス リストの動作とリファレンス リストの構文の詳細については、リファレンス リストのページをご覧ください。

リファレンス リストは、events または outcome セクションで使用できます。ルールで、さまざまな種類のリファレンス リストを使用する構文は次のとおりです。

// STRING reference list
$e.principal.hostname in %string_reference_list

// REGEX reference list
$e.principal.hostname in regex %regex_reference_list

// CIDR reference list
$e.principal.ip in cidr %cidr_reference_list

not $e.principal.hostname in regex %my_regex_list」のように、リファレンス リストで、not 演算子を使用することもできます。

パフォーマンス上の理由から、Detection Engine では、リファレンス リストの使用が制限されています。1 つのルールでは、最大 7 個の in ステートメントを使用できます。また、同じルールに複数のタイプの in ステートメントを使用できます。7 個のうち最大 2 つの in ステートメントで、特別な演算子 regex または cidr を使用できます。

リファレンス リストで nocase 演算子は機能しません。リファレンス リストの呼び出し後に nocase を含めても効果がないため、おすすめしません。

メタセクションの構文

Meta セクションは複数の行で構成され、各行は Key-Value ペアを定義します。キー部分は引用符で囲まない文字列に、値の部分は引用符で囲まれた文字列にする必要があります。

<key> = "<value>"

有効な meta セクション行の例を次に示します。 meta: author = "Chronicle" severity = "HIGH"

events セクションの構文

events セクションに述語のリストを表示して、次の項目を指定します。

  • 各一致変数またはプレースホルダ変数が表すもの
  • 条件としての単純なバイナリ式
  • 条件としての関数式
  • 条件としてリスト式の参照
  • 論理演算子

変数の宣言

変数を宣言する場合は、次の構文を使用します。

  • <EVENT_FIELD> = <VAR>
  • <VAR> = <EVENT_FIELD>

次の例に示すように、どちらも同等です。

  • $e.source.hostname = $hostname
  • $userid = $e.principal.user.userid

この宣言は、この変数がイベント変数に指定されたフィールドを表すことを示します。イベント フィールドが繰り返しフィールドの場合、一致変数は配列内の任意の値を表すことができます。1 つの一致変数またはプレースホルダ変数に複数のイベント フィールドを割り当てることもできます。これは推移的な結合条件です。

たとえば、次のようになります。

  • $e1.source.ip = $ip
  • $e2.target.ip = $ip

これは次と同等です。

  • $e1.source.ip = $ip
  • $e1.source.ip = $e2.target.ip

変数を使用する場合、変数宣言でその変数を宣言する必要があります。変数が宣言なしで使用されていると、コンパイル エラーとみなされます。

条件としての単純なバイナリ式

条件として使用する単純なバイナリ式には、次の構文を使用します。

  • <EXPR> <OP> <EXPR>

式は、イベント フィールド、変数、リテラル、または関数の式のいずれかです。

例:

  • $e.source.hostname = "host1234"
  • $e.source.port < 1024
  • 1024 < $e.source.port
  • $e1.source.hostname != $e2.target.hostname
  • $e1.metadata.collected_timestamp.seconds > $e2.metadata.collected_timestamp.seconds
  • $port >= 25
  • $host = $e2.target.hostname
  • "google-test" = strings.concat($e.principal.hostname, "-test")
  • "email@google.org" = re.replace($e.network.email.from, "com", "org")

両側がリテラルの場合は、コンパイル エラーとみなされます。

条件としての関数式

一部の関数式はブール値を返します。この値は、events セクションの個別の述語として使用できます。該当する関数は次のとおりです。

  • re.regex()
  • net.ip_in_range_cidr()

例:

  • re.regex($e.principal.hostname, `.*\.google\.com`)
  • net.ip_in_range_cidr($e.principal.ip, "192.0.2.0/24")

条件としてリスト式の参照

イベント セクションでは、リファレンス リストを使用できます。詳細については、リファレンス リストをご覧ください。

論理演算子

論理 and と論理 or の演算子は、次の例に示すように events セクションで使用できます。

  • $e.metadata.event_type = "NETWORK_DNS" or $e.metadata.event_type = "NETWORK_DHCP"
  • ($e.metadata.event_type = "NETWORK_DNS" and $e.principal.ip = "192.0.2.12") or ($e.metadata.event_type = "NETWORK_DHCP" and $e.principal.mac = "AB:CD:01:10:EF:22")
  • not $e.metadata.event_type = "NETWORK_DNS"

デフォルトでは、優先度の高い順に notandor となります。

たとえば、"a or b and c""a or (b and c)" と評価されます。必要に応じて、括弧を使用して優先順位を変更できます。

events セクションでは、すべての述語はデフォルトで and であるとみなされます。

イベント内の演算子

演算子は列挙型で使用できます。ルールに適用して、パフォーマンスを簡素化、最適化できます(参照リストの代わりに演算子を使用します)。

次の例では、「USER_UNCATEGORIZED」と「USER_RESOURCE_DELETION」は 15000 と 15014 に対応しているため、ルールによって一覧表示されたイベントがすべて検索されます。

$e.metadata.event_type >= "USER_CATEGORIZED" and $e.metadata.event_type <= "USER_RESOURCE_DELETION"

イベントのリスト

  • USER_RESOURCE_DELETION
  • USER_RESOURCE_UPDATE_CONTENT
  • USER_RESOURCE_UPDATE_PERMISSIONS
  • USER_STATS
  • USER_UNCATEGORIZED

修飾キー

nocase

文字列値または正規表現間の比較式がある場合は、式の最後に nocase を追加することで大文字と小文字の区別を無視できます。

  • $e.principal.hostname != "http-server" nocase
  • $e1.principal.hostname = $e2.target.hostname nocase
  • $e.principal.hostname = /dns-server-[0-9]+/ nocase
  • re.regex($e.target.hostname, `client-[0-9]+`) nocase

フィールドの型が列挙値の場合、これは使用できません。次の例は無効であり、コンパイル エラーになります。

  • $e.metadata.event_type = "NETWORK_DNS" nocase
  • $e.network.ip_protocol = "TCP" nocase

繰り返しフィールド

any, all

UDM と Entity では、一部のフィールドが繰り返しとしてラベル付けされ、値やその他の種類のメッセージのリストであることを示します。YARA-L では、繰り返しフィールドの各要素が個別に処理されます。つまり、ルールで繰り返しフィールドが使用されている場合、フィールド内の各要素についてルールを評価します。予期しない動作が起こる可能性があります。たとえば、ルールの events セクションに $e.principal.ip = "1.2.3.4"$e.principal.ip = "5.6.7.8" の両方がある場合、"1.2.3.4""5.6.7.8" の両方が principal.ip にある場合でも、ルールは一致を生成しません

繰り返しフィールド全体を評価するには、any 演算子と all 演算子を使用します。any を使用すると、繰り返しフィールドの値のいずれかが条件を満たした場合に、述語が true と評価されます。all を使用すると、繰り返しフィールドのすべての値が条件を満たす場合、述語は true と評価されます。

  • any $e.target.ip = "127.0.0.1"
  • all $e.target.ip != "127.0.0.1"
  • re.regex(any $e.about.hostname, `server-[0-9]+`)
  • net.ip_in_range_cidr(all $e.principal.ip, "10.0.0.0/8")

any 演算子と all 演算子は、繰り返しフィールドでのみ使用できます。また、繰り返しフィールドをプレースホルダ変数に割り当てるときや、別のイベントのフィールドと結合するときには使用できません。

たとえば、any $e.principal.ip = $ipany $e1.principal.ip = $e2.principal.ip は有効な構文ではありません。繰り返しフィールドを照合または結合するには、$e.principal.ip = $ip を使用します。繰り返しフィールドの要素ごとに、1 つの一致変数値(結合)があります。

any または all を使用して条件を記述する場合、not で条件を否定するときには、否定演算子を使用する場合と意味が異なる可能性があります。

次に例を示します。

  • not all $e.principal.ip = "192.168.12.16" は、一部の IP アドレスが "192.168.12.16" と一致していないかどうかをチェックします。つまり、このルールでは、いずれかの IP アドレスが "192.168.12.16" と一致しないことが確認されます。
  • all $e.principal.ip != "192.168.12.16" は、すべての IP アドレスが "192.168.12.16" と一致していないかどうかをチェックします。つまり、このルールでは、"192.168.12.16" に一致する IP アドレスがないことが確認されます。

イベント変数の結合要件

ルールで使用するすべてのイベント変数は、次のいずれかの方法で他のすべてのイベント変数と結合される必要があります。

  • 結合された 2 つのイベント変数のイベント フィールドを比較して直接結合する(例: $e1.field = $e2.field)。式には算術演算または関数呼び出しを含めないでください。

  • イベント フィールドのみを含む推移的結合によって間接的に結合する(「推移的結合」の定義については、変数の宣言をご覧ください)。式には算術演算または関数呼び出しを含めないでください。

たとえば、$e1、$e2、$e3 がルールで使用されている場合、次の events セクションは有効です。

events:
  $e1.principal.hostname = $e2.src.hostname // $e1 joins with $e2
  $e2.principal.ip = $e3.src.ip // $e2 joins with $e3
events:
  // all of $e1, $e2 and $e3 are transitively joined via the placeholder variable $ip
  $e1.src.ip = $ip
  $e2.target.ip = $ip
  $e3.about.ip = $ip
events:
  $e1.principal.hostname = $e2.src.hostname // $e1 joins with $e2

  // Function to event comparison is not a valid join condition for $e1 and $e2,
  // but the whole events section is valid because we have a valid join condition in the first line.
  re.capture($e1.src.hostname, ".*") = $e2.target.hostname

ただし、以下は無効な events セクションの例です。

events:
  // Event to function comparison is an invalid join condition for $e1 and $e2.
  $e1.principal.hostname = re.capture($e2.principal.application, ".*")
events:
  // Event to arithmetic comparison is an invalid join condition for $e1 and $e2.
  $e1.principal.port = $e2.src.port + 1
events:
  $e1.src.ip = $ip
  $e2.target.ip = $ip
  $e3.about.ip = "192.1.2.0" //$e3 is not joined with $e1 or $e2.
events:
  $e1.src.ip = $ip

  // Function to placeholder comparison is an invalid transitive join condition.
  re.capture($e2.target.ip, ".*") = $ip
events:
  $e1.src.port = $port

  // Arithmetic to placeholder comparison is an invalid transitive join condition.
  $e2.principal.port + 800 = $port

一致セクションの構文

一致条件を確認する前に、match セクションでグループ イベントの一致変数を一覧表示します。これらのフィールドは、一致するたびに返されます。

  • 各一致変数が表す内容を events セクションで指定します。
  • over キーワードに続くイベントを相関させる期間を指定します。期間外のイベントは無視されます。
  • 期間を指定するには、構文 <number><s/m/h/d> を使用します。ここで、s/m/h/d は秒、分、時、日を表します。
  • 指定できる最小時間は 1 分です。
  • 指定できる最大時間は 48 時間です。

有効な match の例を次に示します。

$var1, $var2 over 5m

このステートメントは、ルールが一致した場合に $var1$var2events セクションで定義)を返します。指定する時間は 5 分です。間隔が 5 分を超えるイベントは相関されないため、ルールで無視されます。

有効な match の別の例を示します。

$user over 1h

次のステートメントは、ルールが一致した場合に $user を返します。指定する期間は 1 時間です。1 時間を超えるイベントは相関されません。これらのイベントは、ルールで検出対象と見なされません。

有効な match の別の例を示します。

$source_ip, $target_ip, $hostname over 2m

次のステートメントは、ルールが一致した場合に $source_ip$target_ip$hostname を返します。指定する期間は 2 分です。間隔が 2 分を超えるイベントは相関されません。これらのイベントは、ルールで検出対象と見なされません。

次の例は、無効な match セクションを示しています。

  • var1, var2 over 5m // invalid variable name
  • $user 1h // missing keyword

スライディング ウィンドウ

デフォルトでは、YARA-L 2.0 ルールはホップ ウィンドウを使用して評価されます。エンタープライズ イベントデータの時間範囲は、match ウィンドウで指定された期間を含む、重複するホップ ウィンドウのセットに分割されます。その後、イベントは各ホップ ウィンドウ内で関連付けられます。ホップ ウィンドウでは、特定の順序で発生するイベントを検索することはできません(たとえば、e1e2 から 2 分後までに発生します)。イベント e1 の発生とイベント e2 の発生は、それらが互いのホップ ウィンドウ期間内にある限り相関します。

ルールは、スライディング ウィンドウを使用して評価することもできます。スライディング ウィンドウを使用すると、指定されたピボット イベント変数の開始時または終了時に、match セクションで指定された期間のスライディング ウィンドウが生成されます。次に、各スライディング ウィンドウ内でイベントが相互に関連付けられます。これにより、特定の順序で発生するイベントを検索できます(たとえば、e1e2 から 2 分後までに発生します)。イベント e1 がイベント e2 の後のスライディング ウィンドウ期間内に発生した場合、イベント e1 の発生とイベント e2 の発生は相関します。

ルールの match セクションで、スライディング ウィンドウを次のように指定します。

<match-var-1>, <match-var-2>, ... over <duration> before|after <pivot-event-var>

ピボット イベント変数は、スライディング ウィンドウに基づくイベント変数です。before キーワードを使用する場合、スライディング ウィンドウが生成され、ピボット イベントが発生するたびに終了します。after キーワードを使用する場合、ピボット イベントが発生するたびにスライディング ウィンドウが生成されます。

有効なスライディング ウィンドウの使用方法の例を次に示します。

  • $var1, $var2 over 5m after $e1
  • $user over 1h before $e2

結果セクションの構文

outcome セクションで、最大 20 個の結果変数を任意の名前で定義できます。これらの結果は、ルールによって生成された検出に保存されます。各検出は、結果に対して異なる値を持つ場合があります。

結果名 $risk_score は特別なものです。オプションでこの名前を使用して結果を定義できます。定義する場合は、整数型にする必要があります。入力すると、ルール検出から発生したアラートの Enterprise Insights ビューrisk_score が表示されます。

結果変数のデータ型

各結果変数には異なるデータ型を指定できます。これは、計算に使用した式によって決まります。サポートされている結果のデータ型は次のとおりです。

  • integer
  • 文字列
  • 整数のリスト
  • 文字列のリスト

条件付きロジック

条件付きロジックを使用して、結果の値を計算できます。条件は、次の構文パターンを使用して指定されます。

if(BOOL_CLAUSE, THEN_CLAUSE)
if(BOOL_CLAUSE, THEN_CLAUSE, ELSE_CLAUSE)

条件式は、「BOOL_CLAUSE が true の場合は、THEN_CLAUSE を返し、それ以外の場合は ELSE_CLAUSE を返す」と読むことができます。

BOOL_CLAUSE は、ブール値に評価される必要があります。BOOL_CLAUSE 式は、events セクションの式と同様の形式を取ります。たとえば、次のものが含まれます。

  • 比較演算子を使用した UDM フィールド名。例:

    if($context.graph.entity.user.title = "Vendor", 100, 0)

  • プレースホルダ変数(events セクション内で定義したものなど)。例:

    if($severity = "HIGH", 100, 0)

  • ブール値を返す関数。例:

    if(re.regex($e.network.email.from, .*altostrat\.com), 100, 0)

  • リファレンス リストからの検索。例:

    if($u.principal.hostname in %my_reference_list_name, 100, 0)

THEN_CLAUSE と ELSE_CLAUSE は同じデータ型でなければなりません。整数と文字列がサポートされています。

データ型が整数の場合は、ELSE_CLAUSE を省略できます。省略した場合、ELSE_CLAUSE は 0 と評価されます。例:

`if($e.field = "a", 5)` is equivalent to `if($e.field = "a", 5, 0)`

データ型が文字列の場合は、ELSE_CLAUSE を指定する必要があります。

算術演算

数学演算を使用して、整数データ型の結果を計算できます。結果計算の最上位の演算子として、加算と減算をサポートしています(乗算、除算、剰余はサポートしていません)。例:

outcome:
  $risk_score = max(100 + if($severity = "HIGH", 10, 5) - if($severity = "LOW", 20, 0))

結果内のプレースホルダ変数

結果変数を計算するときは、ルールのイベント セクションで定義されているプレースホルダ変数を使用できます。この例では、ルールのイベント セクションで $email_sent_bytes が定義されていると仮定します。

単一イベントの例:

// No match section, so this is a single-event rule.

outcome:
  // Use placeholder directly as an outcome value.
  $my_outcome = $email_sent_bytes

  // Use placeholder in a conditional.
  $other_outcome = if($file_size > 1024, "SEVERE", "MODERATE")

condition:
  $e

マルチイベントの例:

match:
  // This is a multi event rule with a match section.
  $hostname over 5m

outcome:
  // Use placeholder directly in an aggregation function.
  $max_email_size = max($email_sent_bytes)

  // Use placeholder in a mathematical computation.
  $total_bytes_exfiltrated = sum(
    1024
    + $email_sent_bytes
    + $file_event.principal.file.size
  )

condition:
  $email_event and $file_event

集約

結果セクションは、マルチイベント ルール(一致セクションを含むルール)と単一イベントルール(一致セクションを含まないルール)で使用できます。集計の要件は次のとおりです。

  • マルチイベント ルール(一致セクションあり)

    • 結果を計算する式は、特定の検出結果を生成したすべてのイベントに対して評価されます。
    • 式は集計関数でラップする必要があります。
      • 例: $max_email_size = max($e.network.sent_bytes)
      • 式に繰り返しフィールドが含まれている場合、集計は、繰り返しフィールド内のすべての要素に対して、検出を生成したすべてのイベントを対象に行われます。
  • 単一イベントのルール(一致セクションなし)

    • 結果を計算する式は、特定の検出結果を生成した単一のイベントに対して評価されます。
    • 少なくとも 1 つの繰り返しフィールドを含む式には、集計関数を使用する必要があります
      • 例: $suspicious_ips = array($e.principal.ip)
      • 集計は繰り返しフィールド内のすべての要素に対して行われます
    • 繰り返しフィールドのない式には集計関数を使用できません
      • 例: $threat_status = if($e.principal.file.size > 1024, "SEVERE", "MODERATE")

次の集計関数を使用できます。

  • max(): 可能性のあるすべての値の最大値を出力します。整数でのみ使用できます。
  • min(): 可能性のあるすべての値の最小値を出力します。整数でのみ使用できます。
  • sum(): 可能性のあるすべての値の合計を出力します。整数でのみ使用できます。
  • count_distinct(): 可能性のあるすべての値を収集し、可能性のある値の一意の値の数を出力します。
  • count(): count_distinct() のように動作しますが、可能性のある値の一意でない値の数を返します。
  • array_distinct(): 可能性のあるすべての値を収集し、これらの値のリストを出力します。値のリストを 25 個のランダムな要素に切り詰めます。
  • array(): array_distinct() のように動作しますが、一意でない値のリストを返します。また、値のリストを 25 個のランダムな要素に切り詰めます。

集計関数は、検出を生成したすべてのイベントで動作するため、複数のイベントが存在することを指定する condition セクションがルールに含まれている場合に重要です。

たとえば、outcome セクションと condition セクションに次の内容が含まれているとします。

outcome:
  $asset_id_count = count($event.principal.asset_id)
  $asset_id_distinct_count = count_distinct($event.principal.asset_id)

  $asset_id_list = array($event.principal.asset_id)
  $asset_id_distinct_list = array_distinct($event.principal.asset_id)

condition:
  #event > 1

条件セクションは検出ごとに複数の event を必要とするため、集計関数は複数のイベントを処理します。次のイベントで 1 つの検出結果が生成されたとします。

event:
  // UDM event 1
  asset_id="asset-a"

event:
  // UDM event 2
  asset_id="asset-b"

event:
  // UDM event 3
  asset_id="asset-b"

この場合、結果の値は次のようになります。

  • $asset_id_count = 3
  • $asset_id_distinct_count = 2
  • $asset_id_list = ["asset-a", "asset-b", "asset-b"]`
  • $asset_id_distinct_list = ["asset-a", "asset-b"]

結果セクションを使用する際の注意事項:

その他の注意事項と制限事項:

  • outcome セクションでは、events セクションでまだ定義されていない新しいプレースホルダ変数を参照できません。
  • outcome セクションでは、events セクションで定義されていないイベント変数を使用できません。
  • イベント フィールドが属するイベント変数が events セクションですでに定義されている場合、outcome セクションでは、events セクションで使用されていないイベント フィールドを使用できます。
  • outcome セクションで関連付けることができるのは、events セクションですでに関連付けられているイベント変数のみです。相関関係は、異なるイベント変数からの 2 つのイベント フィールドが等価である場合に発生します。

YARA-L 2.0 の概要の結果セクションを使用して例を確認できます。 結果セクションによる検出の重複排除の詳細については、コンテキストアウェア分析の作成をご覧ください。

条件セクションの構文

condition セクションでは、次のことができます。

  • events セクションで定義されたイベントとプレースホルダに対して一致条件を指定します。詳しくは、次のセクションのイベントとプレースホルダの条件をご覧ください。
  • (省略可)and キーワードを使用して、outcome セクションで定義されている結果変数を使用して一致条件を指定します。詳しくは、次のセクションの結果の条件をご覧ください。

有効な条件パターンは次のとおりです。

condition:
  <event/placeholder conditionals>
condition:
  <event/placeholder conditionals> and <outcome conditionals>

イベントとプレースホルダの条件

キーワード and または or と結合されたイベントとプレースホルダ変数の条件述語を一覧表示します。

次の条件は境界条件です。関連付けられたイベント変数を強制的に存在するようにします。つまり、検出によりイベントが少なくとも 1 回出現する必要があります。

  • $var // equivalent to #var > 0
  • #var > n // where n >= 0
  • #var >= m // where m > 0

次の条件は境界なし条件です。関連付けられたイベント変数が存在しないようにします。つまり、検出でイベントのオカレンスがない可能性があります。これにより、不在ルールを作成できます。このルールでは、変数の存在ではなく、変数の不在を検索します。

  • !$var // equivalent to #var = 0
  • #var >= 0
  • #var < n // where n > 0
  • #var <= m // where m >= 0

次の例では、変数(イベント変数またはプレースホルダ変数)の特殊文字 # が、その変数の個々のイベントまたは値の数を表します。

$e and #port > 50 or #event1 > 2 or #event2 > 1 or #event3 > 0

次の不在の例も有効で、$event1 とは異なるイベントが 3 つ以上存在し、$event2 とは異なるイベントがない場合に true と評価されます。

#event1 > 2 and !$event2

無効な述語の例を以下に示します。

  • $e, #port > 50 // incorrect keyword usage
  • $e or #port < 50 // or keyword not supported with non-bounding conditions
  • not $e // not keyword is not allowed for event and placeholder conditions

結果の条件

出力変数の条件述語を一覧表示します。キーワード and または or と結合、またはキーワード not が前に付加されています。

結果変数のタイプに応じて、結果の条件を次のように指定します。

  • 整数: 演算子 =, >, >=, <, <=, != を使用して整数リテラルと比較します。次に例を示します。

    $risk_score > 10

  • 文字列: = または != の文字列リテラルと比較します。次に例を示します。

    $severity = "HIGH"

  • 整数または配列のリスト: arrays.contains 関数を使用して条件を指定します。次に例を示します。

    arrays.contains($event_ids, "id_1234")

ルールの分類

一致セクションのあるルールで結果条件を指定すると、そのルールがルール割り当てのマルチイベントルールとして分類されます。 単一イベントとマルチイベントの分類について詳しくは、単一イベントルールマルチイベントルールをご覧ください。

カウント(#)文字

# 文字は condition セクションの特殊文字です。イベント名またはプレースホルダ変数名の前で使用する場合は、すべての events セクション条件を満たす固有のイベントまたは値の数を表します。

値($)文字

$ 文字は、condition セクションのもう 1 つの特殊文字です。結果変数名の前に使用すると、その結果の値を表します。

イベント名またはプレースホルダ変数名の前に使用する場合は(例: $event)、#event > 0 の省略形です。

options セクションの構文

options セクションでは、ルールのオプションを指定できます。options セクションの構文は meta セクションの構文と同様です。ただし、キーは事前定義されたオプション名のいずれかでなければならず、値は文字列型に限定されません。

現在利用可能なオプションは allow_zero_values のみです。

  • allow_zero_value — true に設定した場合、ルールによって生成された一致に、一致変数値としてゼロの値を含めることができます。イベント フィールドが空の場合、ゼロ値がイベント フィールドに指定されます。 このオプションはデフォルトで false に設定されています。

有効な options セクション行は次のとおりです。

  • allow_zero_values = true

型チェック

インターフェース内でルールを作成する際に、Chronicle は YARA-L 構文に対して型チェックを行います。表示される型チェックエラーは、ルールが意図したとおりに機能するように修正する際に活用できます。

無効な述語の例を以下に示します。

// $e.target.port is of type integer which cannot be compared to a string.
$e.target.port = "80"

// "LOGIN" is not a valid event_type enum value.
$e.metadata.event_type = "LOGIN"