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"),则该规则可能会触发假正例,因为“排除”数据尚未应用。
补全运行
这些后台运行会在延迟(例如 45 分钟或 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 专业人士的解答。