YARA-L 2.0 已知问题
本文档面向希望调试规则逻辑并优化 YARA-L 2.0 执行的检测工程师。它介绍了如何处理非标准引擎行为,例如字段取消嵌套、聚合中的笛卡尔积扩展以及丰富化最终一致性。按照这些方法,您可以防止因逻辑错误而导致结果值膨胀或检测遗漏。
YARA-L 2.0 使用特定的执行模型,在评估期间,重复字段会扩展为单独的事件行。由于此转换发生在引擎级别,因此引用多个重复字段或对无符号 UDM 类型执行算术运算需要使用特定的语法变通方法,以避免编译器错误或不正确的结果集。本文档概述了这些技术限制以及解决这些问题所需的逻辑模式。
准备工作
在测试或修改 YARA-L 2.0 规则之前,请确保您的账号拥有以下技术权利:
所需 IAM 角色
roles/chronicle.viewer(安全运维查看者):用于查看现有规则和检测元数据。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.20, 192.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)中的重复字段时,取消嵌套会同时发生在父级和子级。这会导致所有元素的笛卡尔积。
在以下示例中,事件 $e 在 security_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 |
ALLOW |
aaaaaaaaa |
Google SecOps |
FAIL |
aaaaaaaaa |
Google SecOps |
CHALLENGE |
aaaaaaaaa |
Google SecOps |
BLOCK |
对无关字段的影响
当规则引用一个或多个重复字段,且父字段也是重复字段时,规则评估中的这种取消嵌套行为可能会产生意外的结果聚合。非不同聚合(例如 sum()、array() 和 count())无法考虑取消嵌套行为在同一事件的其他字段上产生的重复值。
在以下示例中,事件 $e 具有单个主机名 (google.com),但结果 (hostnames) 会聚合到同一事件 $e 的四个取消嵌套实例中,每个实例都有重复的 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 |
ALLOW |
aaaaaaaaa |
Google SecOps |
google.com |
FAIL |
aaaaaaaaa |
Google SecOps |
google.com |
CHALLENGE |
aaaaaaaaa |
Google SecOps |
google.com |
BLOCK |
取消嵌套行为的变通方法
为了确保在发生取消嵌套时结果值准确无误,请使用所选聚合的不同版本。以下函数会忽略取消嵌套创建的重复行:
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
这会导致潜在的最大总和为 6,即使 $e2 只能对应于 3 个不同的事件也是如此。
这会影响总和、计数和数组。对于计数和数组,使用 count_distinct 或 array_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
标准整数常量默认使用有符号整数,这与定义为无符号整数的 UDM 字段(例如 network.received_bytes)不兼容。
变通方法
您可以通过强制整数常量通过除法运算充当浮点数来绕过此错误。
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" AND $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 优先考虑提取速度,以便尽快显示初始提醒。不过,后台丰富化进程(例如解析 GeoIP、ASN 或 UDM 元数据)遵循最终一致性模型。
初始运行 (T₀)
实时引擎可能会在后台丰富化完成之前评估规则。根据您的逻辑是否依赖于丰富化字段进行检测或排除,这可能会导致以下暂时性差异:
假负例(检测延迟):这是一个常见结果。如果规则依赖于丰富化字段来触发(例如
target.user.department == "Finance"),并且该字段为null,则该规则在初始运行期间不匹配。假正例(排除遗漏):如果您的规则使用丰富化字段来过滤掉已知良好的活动(例如
NOT target.ip_geo_country == "US"),则该规则可能会触发假正例,因为“排除”数据尚未应用。
调整运行
这些后台运行会在延迟(例如 4 小时或 30 小时)后重新评估数据。这会按如下方式“调整”检测状态:
延迟检测:在 T₀ 时为“假负例”的事件现在会在丰富化完成后生成检测。
更正:任何 T₀ 假正例都保留在系统中,但完全丰富化的数据在 UDM 查看器中可见,以便进行手动分诊。
运行测试差异
运行测试 工具对已协调的历史数据进行操作。由于在您运行手动测试时数据已完全丰富化,因此您可以立即看到“调整”结果。这意味着您不会看到在实时初始运行期间发生的 T₀ 假负例或基于排除的假正例。
错误修复
使用下表解决实时提醒和测试结果之间的差异。
| 问题 | 说明 | 可操作的修复 |
|---|---|---|
| 排除失败 | 尽管有排除(例如 != "ASN_123"),但规则仍会触发,因为该字段在初始运行期间为 null。 |
向事件部分添加非 null 检查,以确保在评估之前丰富化数据,例如:$e.principal.ip_geo_artifact.network.asn != ""
|
| 实时与测试匹配 | 实时规则会触发提醒,但对相同数据运行测试会显示 "No Results"。 |
添加 $e.field != "",该代码会检查所有丰富化字段(GeoIP、ASN、File Path),以同步实时行为和历史行为。 |
| 元数据缺失 | 检测会显示在信息中心内,其中 GeoIP 或 File Path 字段为空。 |
对于 T0 运行,这是预期行为。如需修复此问题,请添加 field != "" 检查,或在规则时间表中配置结算延迟,以便为提取留出更多时间。
|
验证和测试
如需验证规则是否正确处理延迟丰富化,请执行以下操作:
确定延迟 :找到您认为是假正例的检测。在检测类型 列中,检查是否有
<span class="material-icons">lightbulb</span>图标。没有此图标的提醒来自主运行,其中丰富化延迟最为常见。更新规则逻辑:为逻辑中使用的所有丰富化数据点添加
field != ""检查。
示例(文件路径):
$e.target.process.parent_process.file.full_path != ""测试和验证 :
- 使用运行测试 功能,确保您的逻辑仍与预期的历史数据匹配。
- 验证规则现在是否仅在丰富化字段填充后在调整运行期间触发(或正确排除)。
如需了解详情,请参阅了解规则运行时间表和为规则配置自定义时间表。
需要更多帮助?获得社区成员和 Google SecOps 专业人士的解答。