YARA-L 2.0 已知问题

本文档适用于想要调试规则逻辑并优化 YARA-L 2.0 执行的检测工程师。本文介绍了如何处理非标准引擎行为,例如字段取消嵌套、聚合中的笛卡尔积扩展以及富集最终一致性。通过遵循这些方法,您可以防止导致结果值过高或检测遗漏的逻辑错误。

YARA-L 2.0 使用特定的执行模型,其中重复字段在评估期间会扩展为单独的事件行。由于此转换发生在引擎级别,因此引用多个重复字段或对无符号 UDM 类型执行算术运算需要使用特定的语法解决方法,以避免编译器错误或错误的结果集。本文档概述了这些技术限制以及解决这些限制所需的逻辑模式。

准备工作

在测试或修改 YARA-L 2.0 规则之前,请确保您的账号具有以下技术授权:

所需 IAM 角色

  • roles/chronicle.viewer(Security Operations 查看者):查看现有规则和检测元数据。
  • roles/chronicle.editor(安全运营编辑器):用于修改规则逻辑并保存更改。

所需权限

  • chronicle.rules.runTest:在历史数据上执行运行测试功能时需要此参数。

  • chronicle.detections.get:用于在检测信息中心内检查未嵌套事件的输出。

主要术语

  • UDM(统一数据模型):用于在整个平台中构建所有提取的安全遥测数据的标准化架构。
  • 取消嵌套:在引擎级别将包含重复字段(数组)的单个 UDM 事件展开为多行。每一行都代表数组中的一个唯一元素,这可能会导致在规则评估期间出现行相乘的情况。
  • T₀(初始运行):规则对传入遥测数据的首次执行。这种情况发生在“流式传输”阶段,通常在后台丰富处理(例如 GeoIP 或 ASN 真实性检查)完成之前。

包含重复字段取消嵌套的结果汇总

如果规则引用了具有多个元素的事件变量中的重复字段,则每个元素都会拆分为单独的事件行。

例如,事件 $e 中重复字段 target.ip 中的两个 IP 地址会拆分为两个 $e 实例,每个实例都有不同的 target.ip 值。

rule outbound_ip_per_app {
  meta:

  events:
    $e.principal.application = $app

  match:
    $app over 10m

  outcome:
    $outbound_ip_count = count($e.target.ip) // yields 2.

  condition:
    $e
}

事件记录:取消嵌套之前和之后

本部分中的表格演示了如何将包含 IP 地址数组的单个事件转换为两条不同的记录。

取消嵌套之前

下表显示了在取消嵌套重复字段之前的事件记录:

metadata.id principal.application target.ip
aaaaaaaaa Google SecOps [192.0.2.20192.0.2.28]

取消嵌套后

下表显示了取消嵌套重复字段后的事件记录:

metadata.id principal.application target.ip
aaaaaaaaa Google SecOps 192.0.2.20
aaaaaaaaa Google SecOps 192.0.2.28

嵌套的重复字段(笛卡尔积)

当规则引用嵌套在另一个重复字段中的重复字段(例如 security_results.action)时,系统会在父级和子级同时进行非嵌套。这会生成所有元素的笛卡尔积。

在以下示例中,一个事件 $esecurity_results 上有两个重复值,在 security_results.actions 上有两个重复值,这些值被取消嵌套为四个实例。

rule security_action_per_app {
  meta:

  events:
    $e.principal.application = $app

  match:
    $app over 10m

  outcome:
    $security_action_count = count($e.security_results.actions) // yields 4.

  condition:
    $e
}

嵌套取消嵌套之前的事件记录

原始记录将操作存储在嵌套的数组结构中。

metadata.id principal.application security_results
aaaaaaaaa Google SecOps [ { actions: [ ALLOW, FAIL ] }{ actions: [ CHALLENGE, BLOCK ] } ]

嵌套取消嵌套后的事件记录

展开后,每个唯一操作都会成为一行,这可能会导致非去重汇总中的数量出现意外情况。

metadata.id principal.application security_results.actions
aaaaaaaaa Google SecOps 允许
aaaaaaaaa Google SecOps 未通过
aaaaaaaaa Google SecOps 挑战
aaaaaaaaa Google SecOps 屏蔽

对无关字段的影响

在规则评估中,这种取消嵌套行为可能会在规则引用一个或多个重复字段(父字段也是重复字段)时产生意外的结果汇总。非去重聚合函数(如 sum()array()count())无法考虑因取消嵌套行为而产生的同一事件中其他字段的重复值。

在以下示例中,事件 $e 具有单个主机名 (google.com),但结果 (hostnames) 会针对同一事件 $e 的 4 个未嵌套实例进行汇总,每个实例都具有重复的 principal.hostname 值。由于 security_results.actions 上重复的值被取消嵌套,因此结果会生成四个主机名(而不是一个)。

rule security_action_per_app {
  meta:

  events:
    $e.principal.application = $app

  match:
    $app over 10m

  outcome:
    $hostnames = array($e.principal.hostname) // yields 4.
    $security_action_count = count($e.security_results.action) // yields 4.

  condition:
    $e
}

取消嵌套之前包含无关字段的事件记录

主机名是单个值,但它与重复的安全结果并列。

metadata.id principal.application principal.hostname security_results
aaaaaaaaa Google SecOps google.com [ { action: [ ALLOW, FAIL ] }{ action: [ CHALLENGE, BLOCK ] } ]

取消嵌套后包含无关字段的事件记录

现在,主机名在四行中重复出现,导致 array() 函数拾取了四次。

metadata.id principal.application principal.hostname security_results.action
aaaaaaaaa Google SecOps google.com 允许
aaaaaaaaa Google SecOps google.com 未通过
aaaaaaaaa Google SecOps google.com 挑战
aaaaaaaaa Google SecOps google.com 屏蔽

针对取消嵌套行为的解决方法

为确保在取消嵌套时结果值准确无误,请使用所选聚合的去重版本。以下函数会忽略因取消嵌套而创建的重复行:

  • max()
  • min()
  • array_distinct()
  • count_distinct()

包含多个事件变量的结果汇总

如果规则包含多个事件变量,则在聚合中,检测中包含的每种事件组合都会有一个单独的项。例如,如果针对下列列出的事件运行以下示例规则:

events:
  $e1.field = $e2.field
  $e2.somefield = $ph

match:
  $ph over 1h

outcome:
   $some_outcome = sum(if($e1.otherfield = "value", 1, 0))

condition:
  $e1 and $e2
event1:
  // UDM event 1
  field="a"
  somefield="d"

event2:
  // UDM event 2
  field="b"
  somefield="d"

event3:
  // UDM event 3
  field="c"
  somefield="d"

系统会针对所有事件组合计算总和,以便您在计算结果值时同时使用这两个事件变量。计算中使用了以下元素:

1: $e1 = event1, $e2 = event2
2: $e1 = event1, $e2 = event3
3: $e1 = event2, $e2 = event1
4: $e1 = event2, $e2 = event3
5: $e1 = event3, $e2 = event1
5: $e1 = event3, $e2 = event2

这样一来,即使 $e2 只能对应 3 个不同的事件,总和也可能达到 6。

这会影响总和、数量和数组。对于计数和数组,使用 count_distinctarray_distinct 可以解决问题,但对于求和,则没有解决方法。

表达式开头的圆括号

不支持以英文圆括号开头表达式,这会在规则编辑器中触发解析错误。

语法无效

parsing: error with token: ")"
invalid operator in events predicate

以下示例会生成此类错误:

($event.metadata.ingested_timestamp.seconds -
$event.metadata.event_timestamp.seconds) / 3600 > 1

有效的语法变体

以下语法变体返回的结果相同,但语法有效:

$event.metadata.ingested_timestamp.seconds / 3600 -
$event.metadata.event_timestamp.seconds / 3600 > 1
    1 / 3600 * ($event.metadata.ingested_timestamp.seconds -
$event.metadata.event_timestamp.seconds) > 1
    1 < ($event.metadata.ingested_timestamp.seconds -
$event.metadata.event_timestamp.seconds) / 3600

结果中的索引数组需要进行聚合

不允许直接对 outcome 部分中重复字段的数组进行索引。这需要一个临时占位变量。

outcome:
  $principal_user_dept = $suspicious.principal.user.department[0]

临时解决方法

events 部分中将特定数组索引捕获到占位符变量中,然后在结果中引用该占位符。

events:
  $principal_user_dept = $suspicious.principal.user.department[0]

outcome:
  $principal_user_department = $principal_user_dept

具有不存在条件的 OR

如果您在两个单独的事件变量之间应用 OR 条件,并且规则匹配的是不存在的情况,则规则会成功编译,但可能会产生假正例检测结果。

例如,以下规则语法可以匹配具有 $event_a.field = "something" 的事件,即使它不应该匹配:

events:
     not ($event_a.field = "something" **or** $event_b.field = "something")
condition:
     $event_a and #event_b >= 0

临时解决方法

将不存在性检查分为每个变量的单独块,以保持逻辑完整性。

events:
  not ($event_a.field = "something")
  not ($event_b.field = "something")

condition:
  $event_a and #event_b >= 0

使用无符号事件字段进行算术运算

如果您尝试在算术运算中使用整数常量与类型为无符号整数的 UDM 字段,则会收到错误。例如:

events:
  $total_bytes = $e.network.received_bytes * 2

标准整数常量默认为有符号整数,这与定义为无符号整数(例如 network.received_bytes)的 UDM 字段不兼容。

临时解决方法

您可以通过除法运算强制使整数常量表现为浮点数,从而绕过此错误。

events:
  $total_bytes = $e.network.received_bytes * (2/1)

GeoIP 丰富和最终一致性

在初始扩充阶段(串流和延迟敏感型),系统会优先考虑速度,而不是立即准确率,这可能会导致数据缺失和潜在的假正例。系统会在后台继续丰富数据,但运行规则时,数据可能尚不可用。这是正常的最终一致性流程的一部分。

为防止因丰富延迟而导致出现误报,请在评估字段值之前明确检查该字段是否为空。

例如,假设有以下规则事件:

$e.principal.ip_geo_artifact.network.asn = "16509" AND
$e.principal.ip_geo_artifact.location.country_or_region = "United Kingdom"

该规则依赖于以下事实:事件必须具有 $e.principal.ip_geo_artifact.network.asn = "16509"$e.principal.ip_geo_artifact.location.country_or_region = "United Kingdom",这两个字段都是丰富字段。如果未及时完成丰富化,规则将产生假正例。

为避免这种情况,此规则的更佳检查方式如下:

$e.principal.ip_geo_artifact.network.asn != "" AND
$e.principal.ip_geo_artifact.network.asn = "16509" AND
$e.principal.ip_geo_artifact.location.country_or_region != "" AND
$e.principal.ip_geo_artifact.location.country_or_region = "United Kingdom"

此规则消除了以下可能性:具有 ASN 16509 但位于英国境外的 IP 触发该事件。这有助于提高规则的总体精确度。

了解如何排查富集延迟问题。

问题排查

本部分概述了性能预期,并针对实时检测行为与测试结果不同的常见问题提供了自助式修复方案。

未来日期的活动

多事件规则旨在按相对于提取的时间顺序处理事件。如果您指定并激活了多事件规则,则该规则不会针对时间戳为未来的事件创建检测结果,例如当 event.timestamp 的日期和时间设置在 ingest.timestamp 之后时。

丰富化延迟时间

Google SecOps 会优先考虑数据提取速度,以便尽快显示初始提醒。不过,解析 GeoIPASNUDM 元数据等后台丰富化流程遵循最终一致性模型。

初始运行(T₀)

实时引擎可能会在后台富集完成之前评估规则。如果您的逻辑依赖于用于检测或排除的丰富字段,则可能会导致以下暂时性差异:

  • 假负例(检测滞后):这是常见的结果。如果某条规则依赖于某个富化字段(例如 target.user.department == "Finance")来触发,并且该字段为 null,则该规则在初始运行期间不会匹配。

  • 假正例(排除项遗漏):如果您的规则使用丰富字段来过滤掉已知良好的活动(例如 NOT target.ip_geo_country == "US"),则该规则可能会触发假正例,因为“排除项”数据尚未应用。

调整跑步

这些后台运行会在延迟一段时间(例如 45 分钟或 30 小时)后重新评估数据。这样可以“校正”检测状态,具体如下:

  • 延迟检测:在 T₀ 时为“假负例”的事件在完成丰富化后现在会产生检测结果。

  • 更正:所有 T₀ 误报仍保留在系统中,但完全丰富的数据会显示在 UDM 查看器中,以便进行人工分诊。

运行测试差异

运行测试工具可对已完成对账的历史数据进行操作。由于在您运行手动测试时,数据已完全丰富,因此您可以立即看到“补齐”结果。这意味着,您不会看到在初始实时运行期间出现的 T₀ 假负例或基于排除的假正例。

错误修复

使用下表解决实时提醒与测试结果之间的差异。

问题 说明 可作为行动依据的修复
排除失败 尽管存在排除项(例如 != "ASN_123"),但规则仍会触发,因为该字段在初始运行期间为 null。 向事件部分添加非 null 检查,以确保在评估之前丰富数据,例如:

$e.principal.ip_geo_artifact.network.asn != ""
正式比赛与测试赛的比较 实时规则会触发提醒,但对相同数据运行测试却显示 "No Results 添加了 $e.field != "",用于检查所有丰富字段(GeoIPASNFile Path),以同步实时行为和历史行为。
缺少元数据 检测结果会显示在信息中心内,但 GeoIPFile Path 字段为空。 对于 T0 运行,这是预期行为。如需修正此问题,请在运行时间表中添加 field != "" 检查,或增加首次运行偏移量,以便有更多时间进行提取。

验证和测试

如需验证规则是否正确处理延迟的丰富化,请执行以下操作:

  1. 确定滞后时间:找到您认为属于假正例的检测结果。在检测类型列中,查找 <span class="material-icons">lightbulb</span> 图标。没有此图标的提醒来自初始运行,此时最常出现富集延迟。

  2. 更新规则逻辑:为逻辑中使用的所有丰富数据点添加 field != "" 检查。
    示例(文件路径)
    $e.target.process.parent_process.file.full_path != ""

  3. 测试和验证

    • 使用运行测试功能,确保您的逻辑仍与预期的历史数据相符。
    • 验证在填充富集字段后,规则是否仅在对账运行期间触发(或正确排除)。

如需了解详情,请参阅管理规则运行时间表为规则配置自定义时间表

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