匹配部分语法

支持的平台:

在 YARA-L 2.0 中,match 部分提供了多事件相关性机制。它通过在特定时间边界内关联用户、IP 地址或文件哈希等常见属性,定义将事件分组为单个检测的逻辑。

您可以在以下使用场景中使用 match 部分:

  • 在规则中关联两个或多个不同的事件。
  • 搜索和信息中心内的汇总数据,例如在特定时间范围内统计登录失败的次数。

定义相关性条件

您可以使用它来定义此相关性的条件,方法是指定以下内容:

  • 分组字段(键):变量(例如 $user$ip),必须在事件(在 events 部分中定义)中具有相同的值才能触发匹配。

  • 时间限制:分组事件必须在某个时长窗口内发生,才能满足规则或聚合条件。在“规则”中,此参数用于定义检测窗口;在“搜索”和“信息中心”中,此参数用于定义聚合或关联窗口。

比较功能要求

下表详细比较了“规则到搜索”和“信息中心”。

功能 规则要求 搜索和信息中心支持
变量类型 必须使用在 events 部分中定义的占位符。 支持占位符和直接 UDM 字段。
时间窗口 定义检测边界。 定义聚合或相关性分桶。
语法 over <number><m/h/d>(例如 10m2h1d over <number><m/h/d>
限制 最小值:1m / 最大值:48h 最小值:1m / 最大值:48h

支持的窗口类型

YARA-L 2.0 使用不同的窗口化行为来确定如何划分时间以及如何对事件进行分组。您可以使用以下支持的时间窗口,按指定的时间粒度match 部分中的事件字段和占位符进行分组。

支持的窗口类型 语法 说明 常见用例
$key over <duration> 重叠的时间间隔(默认行为)。 多个事件的一般相关性。
翻滚 $key by <duration> 固定大小、不重叠且连续的时间间隔。 以最长 1 小时为单位(例如 $user by 30m)量化活动。
滑动 $key over <duration> [before|after] $pivot 以特定“透视”事件为锚点的时间间隔。 严格的依序展示(例如 File Download after Login)。

语法示例:

match:
  $user, $source_ip over 5m  // Groups events by user and IP within a 5-minute window

跳跃窗口

跳跃窗口是多事件规则的默认行为。它会创建重叠的时间间隔,以确保不会错过窗口边界附近发生的事件。

  • 语法$key over <duration>(例如,$user over 30m
  • 使用情形:最适合需要确保捕获特定场景的检测,无论窗口间隔何时开始或结束。
  • 支持:支持在搜索和信息中心(例如 count)中进行汇总。

默认情况下,包含 match 部分的 YARA-L 查询使用跃点窗口来关联一段时间内的多个事件。查询的执行时间范围分为一组固定的重叠跃点窗口。虽然这些窗口的持续时间在 match 部分中指定,但重叠间隔和窗口对齐方式由系统定义,用户无法配置。然后,在每个预定窗口内,事件是相关的。

示例:重叠的跳跃窗口,用于持续相关性分析

以下示例展示了一个在 [1:00, 2:00] 时间范围内运行的查询,其中包含 match 部分 $user over 30m,可能生成的一组重叠跳跃窗口为 [1:00, 1:30]、[1:03, 1:33] 和 [1:06, 1:36]。

rule hop_window_brute_force_example {
meta:
  description = "Detects multiple failed logins within a shifting 30-minute window."
  severity = "Medium"

events:
  $login.metadata.event_type = "USER_LOGIN"
  $login.extensions.auth.auth_status = "FAILURE"
  $login.principal.user.userid = $user

match:
  // This creates the overlapping windows (e.g., 1:00-1:30, 1:03-1:33)
  $user over 30m

condition:
  // This will trigger if 10 or more failures fall into any single 30m hop
  #login >= 10
}

示例:使用跳跃窗口进行多事件关联

以下示例表示大多数多事件规则的默认值。它会捕获在同一大致时间范围内发生的事件。

rule hop_window_example {
meta:
  description = "Detect a user with a failed login followed by a success within 30m"

events:
  $e1.metadata.event_type = "USER_LOGIN"
  $e1.extensions.auth.auth_status = "FAILURE"
  $e1.principal.user.userid = $user

  $e2.metadata.event_type = "USER_LOGIN"
  $e2.extensions.auth.auth_status = "SUCCESS"
  $e2.principal.user.userid = $user

match:
  $user over 30m  // This is a hop window

condition:
  $e1 and $e2
}

示例:跳跃窗口比较

为了识别暴力破解尝试,10m 时间窗口会将所有 USER_LOGIN 次失败尝试归为一组。然后,condition 会评估相应 10 分钟时间段内的计数 (#e) 是否超过您的阈值。

rule failed_logins
{
meta:
  author = "Security Team"
  description = "Detects multiple failed user logins within 10-minute windows."
  severity = "HIGH"

events:
  $e.metadata.event_type = "USER_LOGIN"
  $e.security_result.action = "FAIL"
  $user = $e.target.user.userid

match:
  $user over 10m

condition:
  #e >= 5
}

match 部分查找在 10 分钟 (10m) 时间间隔内在新位置登录失败的用户:

match:
  $user over 10m

翻滚窗口

翻滚窗口将数据流分段为固定大小、不重叠且连续的时间间隔。每个事件的时间戳都属于且仅属于一个窗口。翻滚窗口之间没有重叠。这与滑动窗口或跳跃窗口不同,后者可以具有重叠的时间间隔。

如需实现滚动窗口,请在 match 部分中使用 by 运算符。

翻滚窗口将时间划分为连续的、首尾相接的块。例如:

  • by 1h:为每个小时创建窗口(例如 [00:00:00-00:59:59]、[01:00:00-01:59:59] 等)。
  • by 10m:为每个 10 分钟的时间段创建窗口(例如 [00:00:00-00:09:59]、[00:10:00-00:19:59] 等)。

使用场景

在需要执行以下操作时,请使用滚动窗口:

  • 分析不同且不重叠的时间段内的事件。
  • 确保在每个固定时间间隔内,无论事件发生多少次,都只针对给定实体(由匹配变量定义)生成一次检测结果。
  • 统计固定时间段内的唯一实体数量。

去重行为

滚动窗口的一个关键特征是,对于同一组匹配变量,如何在每个窗口内处理检测结果:

  • 每个窗口一次检测:对于一组给定的匹配变量值(例如,特定的 $userid),任何单个滚动窗口最多只会生成一次检测。
  • 先到先得:在特定时间范围内,最先纳入的满足相应匹配变量集规则条件的事件会触发检测。
  • 去重:在同一窗口实例中,任何后续事件如果符合相应条件,都不会生成额外的检测结果。

语法

match 部分的语法如下:

match:
  $variable by <duration>

其中:

  • $variable 是您要匹配的占位变量。
  • <duration> 是一个数字,后跟时间单位:

    • m:分钟
    • h:小时
    • d:天

    指定的时间下限为 1 分钟,上限为 72 小时或 3 天。

示例

以下规则按 1 小时内不重叠的时间段(例如 [00:00:00-00:59:59]、[01:00:00-01:59:59] 等)对登录进行分组:$userid

rule TumblingWindowExample {
  meta:
    description = "Example using a 1-hour tumbling window"
  events:
    $e.metadata.event_type = "USER_LOGIN"
    $e.principal.user.userid = $userid
  match:
    $userid by 1h
  condition:
    $e
}

以下是 user = "alex" 的检测行为示例:

  • 事件 1:Alex 在 00:30 登录。这属于 [00:00:00-00:59:59] 时间段。系统会针对此窗口为 Alex 生成检测结果。

  • 事件 2:另一位用户 "taylor" 在 00:45 登录。这同样属于 [00:00:00-00:59:59],但由于 $userid 不同,因此系统会在此时间窗口内为 Taylor 生成单独的检测。

  • 事件 3:Alex 在 00:40 再次登录。这仍处于 [00:00:00-00:59:59] 时间段内。由于此窗口期内已检测到 Alex 的事件,因此系统会对此事件进行去重处理。系统不会生成新的检测结果。

  • 事件 4:Alex 在 01:20 登录。这属于下一个时间段 [01:00:00-01:59:59]。系统会针对此新窗口为 Alex 生成新的检测结果。

尽管 Alice 的事件 1 和事件 4 发生时间相差不到 1 小时,但由于事件 4 跨越了固定时间范围边界,因此它们属于不同的检测结果。

滑动窗口

如果您需要搜索按严格的相对顺序发生的事件(例如,e1e2 之后最多两分钟内发生),请使用滑动窗口。与固定窗口不同,滑动窗口由指定 $pivot_event 的每次出现触发,使用以下语法:

  • after:窗口从透视事件的时间戳开始,并向前延伸。
  • before:窗口在透视事件的时间戳处结束,并向后延伸。

在查询的 match 部分中指定滑动窗口,如下所示:

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

  • 语法$key over <duration> before|after $<pivot_event>
  • 分组键:用于将事件关联在一起的通用字段(例如 $user$ip)。
  • 时长:相对于透视事件的时间偏移量(例如 5m1h)。
  • 应用场景
    • 严格的序列:检测需要按顺序执行的攻击链(例如,先创建用户,然后进行权限升级)。
    • 相对时间:查找在“触发”事件(例如,Process Start 事件后 30 秒内发生的 Network Connection 事件)的特定偏移量内发生的事件。
    • 缺席检测:识别在开始事件发生后,必需的“清理”或“心跳”事件何时未能发生(例如,没有相应 End 事件的 Database Backup Start)。

有效的滑动窗口示例

以下示例展示了有效的滑动窗口:

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

示例:使用滑动窗口进行前瞻性相关分析 (after)

以下示例演示了如何检测一系列事件,其中第二个事件必须在主要“触发”事件或透视事件发生后的特定时间范围内发生。这有助于检测快速横向移动或自动后续操作。

rule sliding_window_after_example {
meta:
  description = "Detect a network connection occurring within 1 minute after a suspicious process launch."
  severity = "High"

events:
  $proc.metadata.event_type = "PROCESS_LAUNCH"
  $proc.principal.hostname = $host

  $net.metadata.event_type = "NETWORK_HTTP"
  $net.principal.hostname = $host

match:
  // $proc is the pivot; the 1-minute window starts at the $proc timestamp
  $host over 1m after $proc

condition:
  $proc and $net
}

示例:使用滑动窗口 (before) 的回溯相关性

使用 "before" 滑动窗口来调查导致特定提醒的活动。这通常用于根本原因分析,以确定在检测到严重问题之前立即发生了什么情况。

rule sliding_window_before_example {
meta:
  description = "Identify file modifications occurring in the 5 minutes before a ransomware alert."
  severity = "Critical"

events:
  $file.metadata.event_type = "FILE_MODIFICATION"
  $file.principal.hostname = $host

  $alert.metadata.event_type = "ANTIVIRUS_DETECTION"
  $alert.metadata.product_name = "Premium_AV"
  $alert.principal.hostname = $host

match:
  // $alert is the pivot; the 5-minute window ends at the $alert timestamp
  $host over 5m before $alert

condition:
  $file and $alert
}

性能和最佳实践

滑动窗口需要比标准(跳跃)窗口更多的处理能力,因为它们是针对每次出现透视事件进行计算的,可能会导致性能下降。

请遵循以下准则,以确保规则、搜索和信息中心发挥最佳性能:

  • 优先考虑跃点窗口:除非检测逻辑需要特定的事件序列(先是订单 A,然后是订单 B),否则请使用默认的跃点窗口。仅在事件序列至关重要或您要搜索缺失的事件时使用滑动窗口。

  • 使用时间戳过滤条件来提高性能:如果您只需要确保一个事件发生在另一个事件之后,那么 eventscondition 部分中的时间戳比较通常比滑动窗口更高效,例如:

$e1.metadata.event_timestamp.seconds < $e2.metadata.event_timestamp.seconds

  • 多事件设计:避免为单事件查询使用滑动窗口。滑动窗口专为多事件相关性分析而设计。对于单事件逻辑,请遵循以下准则:

    • 使用多个事件变量并更新 condition 部分。
    • 如果不需要相关性,请完全移除 match 部分。
    • (可选)考虑添加时间戳过滤条件,而不是使用滑动窗口,例如:
      $permission_change.metadata.event_timestamp.seconds < $file_creation.metadata.event_timestamp.seconds

了解时间边界

match 部分会根据您的分组键将事件划分为不同的组。指定的时长定义了每个组的时间边界:

  • 纳入:只有在窗口内的事件才会传递到 condition 评估中,以进行特定匹配。
  • 排除:对于相应匹配组,系统会忽略窗口之外的事件,从而防止不相关的事件触发假正例。

match 部分中的零值

Google SecOps 会隐式过滤掉 match 部分中使用的所有占位符的零值("" 表示字符串,0 表示数字,false 表示布尔值,位置 0 中的值表示枚举类型)。

示例:过滤掉零值

以下示例展示了过滤掉零值的查询。

rule ZeroValuePlaceholderExample {

events:
  // Because $host is used in the match section, the query behaves
  // as if the following predicate was added to the events section:
  // $host != ""
  $host = $e.principal.hostname

  // Because $otherPlaceholder was not used in the match,
  // there is no implicit filtering of zero values for $otherPlaceholder.
  $otherPlaceholder = $e.principal.ip

match:
  $host over 5m

condition:
  $e
}

不过,如果为函数分配了占位变量,查询不会隐式过滤掉 match 部分中使用的占位变量的零值。

如需停用对零值的隐式过滤,您可以在“选项”部分中使用 allow_zero_values 选项。allow_zero_values 选项仅在“规则”中提供。

示例:允许零值

以下示例展示了不会隐式过滤掉 match 部分中所用占位符的零值的查询:

rule AllowZeroValuesExample {

events:
  // Because allow_zero_values is set to true, there is no implicit filtering
  // of zero values for $host.
  $host = $e.principal.hostname

  // Because $otherPlaceholder was not used in the match,
  // there is no implicit filtering of zero values for $otherPlaceholder.
  $otherPlaceholder = $e.principal.ip

match:
  $host over 5m

condition:
  $e

options:
  allow_zero_values = true
}

后续步骤

您可以探索以下资源,继续学习 YARA-L 逻辑,或深入了解高级查询函数:

语法和逻辑

参考和示例

需要更多帮助?获得社区成员和 Google SecOps 专业人士的解答。