YARA-L 2.0 语言语法
本部分介绍 YARA-L 语法的主要元素。另请参阅 YARA-L 2.0 语言概览。
规则结构
对于 YARA-L 2.0,您必须按以下顺序指定变量声明、定义和用法:
- meta
- events
- match(可选)
- outcome(可选)
- condition
- 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.
}
注释
使用两个斜杠字符 (// comment
) 或用斜杠星号 (/* comment */
) 发出的多行注释来指定注释,就像在 C 中那样。
字面
支持非负整数(不含小数点)、字符串、布尔值和正则表达式字面量。
字符串和正则表达式字面量
您可以使用以下任一引号字符将字符串括在 YARA-L 2.0 中。不过,引用文字的解释方式因所使用的文字而异。
双引号 (") - 用于普通字符串。必须包含转义字符。
例如:“hello\tworld”-\t 将被解释为一个标签页反引号 (`) - 用于按字面解释所有字符。
例如:“hello\tworld”-\t 不会被解释为标签页
对于正则表达式,您有两种选择。
如果您想在不使用 re.regex()
函数的情况下直接使用正则表达式,请使用 /regex/
作为正则表达式字面量。
使用 re.regex()
函数时,您还可以将字符串字面量用作正则表达式字面量。请注意,对于双引号字符串字面量,您必须用反斜杠字符转义反斜杠字符,这样看上去可能会很麻烦。
例如,以下正则表达式是等效的:
re.regex($e.network.email.from, `.*altostrat\.com`)
re.regex($e.network.email.from, ".*altostrat\\.com")
$e.network.email.from = /.*altostrat\.com/
Google 建议对正则表达式中的字符串使用英文反引号字符,以便于阅读。
运算符
您可以在 YARA-L 中使用以下运算符:
运算符 | 说明 |
= | 等于/声明 |
!= | 不等于 |
< | 小于 |
<= | 小于或等于 |
> | 大于 |
>= | 大于或等于 |
变量
在 YARA-L 2.0 中,所有变量均表示为 $<variable name>
。
您可以定义以下类型的变量:
事件变量 - 以归一化形式 (UDM) 或实体事件表示事件组。在
events
部分指定事件变量的条件。您可以使用名称、事件来源和事件字段来标识事件变量。允许的来源为udm
(用于规范化事件)和graph
(用于实体事件)。如果省略来源,则udm
设置为默认来源。事件字段表示为 .<field name> 链(例如 $e.field1.field2)。事件字段链始终从顶级来源(UDM 或实体)开始。匹配变量 - 在
match
部分声明。匹配变量会成为查询的分组字段,因为对于每组唯一的匹配变量(以及每个时间范围),都会返回一行。当规则找到匹配项时,将返回匹配变量值。指定每个匹配变量在events
部分中表示的内容。占位符变量 - 在
events
部分声明和定义。占位符变量与匹配变量类似。不过,您可以使用condition
部分中的占位符变量来指定匹配条件。
使用匹配变量和占位符变量可以通过传递联接条件来声明事件字段之间的关系(请参阅事件部分语法了解更多详情)。
关键字
YARA-L 2.0 中的关键字不区分大小写。例如,and
或 AND
是等效的。变量名称不得与关键字冲突。例如,$AND
或 $outcome
无效。
以下关键字是用于检测引擎规则的关键字:rule
、meta
、match
、over
、events
、condition
、outcome
、options
、and
、or
、not
、nocase
、in
、regex
、cidr
、before
、after
、all
、any
、、、、{2/{2/2/2/2/2/2/2}/match
{2/2/2/2}/match
{2/2/2}/match
{2/2/2/2}/match
{2/2/2}if
max
min
sum
array
array_distinct
count
count_distinct
地图
结构体和标签
要在 Struct 和标签中搜索特定键值对,请使用标准映射语法:
// 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 在检测引擎中支持的 YARA-L 2.0 函数。
这些函数可用于规则的以下区域:
events
部分。- 结果部分中条件的
BOOL_CLAUSE
。
字符串函数
Chronicle 支持以下字符串操作函数:
- strings.concat(a, b)
- strings.coalesce(a,b)
- strings.to_lower(stringText)
- strings.to_upper(stringText)
- strings.base64_decode(encodedString)
下面几个部分介绍了如何使用各函数。
串联字符串或整数
返回两个字符串、两个整数的串联或两者的组合。
strings.concat(a, b)
此函数接受两个参数(这些参数可以是字符串或整数)并返回两个值串联组成的字符串。整数会在串联之前转换为字符串。参数可以是字面量或事件字段。如果两个参数都是字段,则这两个特性必须来自同一事件。
以下示例包含字符串变量和字符串字面量作为参数。
"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)
参数可以是字面量、事件字段或函数调用。两个参数都必须是字符串类型。如果两个参数都是字段,则这两个特性必须来自同一事件。
以下示例将字符串变量和字符串字面量作为参数。如果 (1) $e.network.email.from
为 suspicious@gmail.com
或 (2) $e.network.email.from
为空,$e.network.email.to
为 suspicious@gmail.com
,则此条件的评估结果为 true。
"suspicious@gmail.com" = strings.coalesce($e.network.email.from, $e.network.email.to)
以下示例包含嵌套合并调用。该条件会将事件 $e
中的第一个非 null IP 地址与参考列表 ip_watchlist
中的值进行比较。在此调用中,参数的合并顺序与规则条件中枚举的参数顺序相同:
- 首先对
$e.principal.ip
求值。 - 接下来会评估
$e.src.ip
。 - 接下来会评估
$e.target.ip
。 - 最后,如果未设置之前的 IP 字段,字符串“No IP”将作为默认值返回。
strings.coalesce(
strings.coalesce($e.principal.ip, $e.src.ip),
strings.coalesce($e.target.ip, "No IP")
) in %ip_watchlist
以下示例尝试从事件 $e1
和事件 $e2
合并 principal.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 编码的字符串作为参数。如果 encodedString 不是有效的 base64 编码的字符串,则此函数会按原样返回 encodedString。
以下示例在 principal.domain.name 为“dGVzdA==”时会返回 True,这是对字符串“test”进行的 base64 编码。
"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)
此函数接受两个参数:
- stringText:要搜索的原始字符串。
- regex:指示要搜索的模式的正则表达式。
正则表达式可以在括号中包含 0 或 1 个捕获组。如果正则表达式包含 0 个捕获组,则该函数返回第一个整个匹配的子字符串。如果正则表达式包含 1 个捕获组,则它将返回捕获组的第一个匹配的子字符串。定义两个或多个捕获组会返回编译器错误。
在以下示例中,如果 $e.principal.hostname 包含“aaa1bbaa2”,则该示例为 True,因为此函数会返回第一个实例。此示例不包含捕获组。
"aaa1" = re.capture($e.principal.hostname, "a+[1-9]")
以下示例会捕获电子邮件地址中 @ 符号后面的所有内容。如果 $e.network.email.from 字段是 test@google.com
,则此示例会返回 google.com。此示例包含一个捕获组。
"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)
此函数接受三个参数:
- stringText:原始字符串。
- replaceRegex:指示要搜索的模式的正则表达式。
- replacementText:要插入到每个匹配项中的文本。
返回源自原始 stringText 的新字符串,其中与 replaceRegex 中的模式匹配的所有子字符串都会替换为 replacementText 中的值。您可以在 replacementText 中使用反斜杠转义的数字(\1 到 \9),将与带有英文括号的对应组匹配的文本插入到 replaceRegex 模式中。“\0”可用于引用整个匹配文本。
该函数会替换非重叠的匹配项,并优先替换第一个找到的出现项。例如,re.replace("banana", "ana", "111") 会返回字符串“b111na”。
以下示例会捕获电子邮件地址中 @
符号后面的所有内容,将 com
替换为 org
,然后返回结果。请注意使用嵌套函数。
"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])
以下函数会返回 [0, 53] 范围内的整数,表示一年中的某周。一周从星期日开始算起。一年中第一个星期日之前的日期属于第 0 周。
timestamp.get_week(unix_seconds [, time_zone])
这些时间提取函数具有相同的参数。
- unix_seconds 是一个表示经过 Unix 计时原点的秒数(例如
$e.metadata.event_timestamp.seconds
)的整数,或一个包含该值的占位符。 - time_zone 是可选的,是表示 time_zone 的字符串。如果省略,则默认值为“GMT”。您可以使用字符串字面量来指定时区。选项包括:
- TZ 数据库名称,例如“America/Los_Angeles”。如需了解详情,请参阅本页中的“TZ 数据库名称”列
- 相对于世界协调时间 (UTC) 的时区偏离值,格式为
(+|-)H[H][:M[M]]
,例如“-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 说明符的示例,您可以将其作为第二个参数传递给时间提取函数:
"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)
网络函数
IP 子网搜索
如果给定 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 地址范围内的单个事件
数组函数
数组长度
返回重复字段元素的数量。
arrays.length($e.principal.ip) = 2
如果路径上有多个重复字段,则返回重复字段元素的总数。
arrays.length($e.intermediary.ip) = 3
函数到占位符分配
您可以在 events
部分中将函数调用的结果分配给占位符。例如:
$placeholder = strings.concat($e.principal.hostname, "my-string").
然后,您可以在 match
、condition
和 outcome
部分中使用占位符变量。但是,函数到占位符的分配有两个限制:
函数到占位符分配的每个占位符都必须分配给包含事件字段的表达式。例如,以下示例有效:
$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.
函数调用应依赖于一个且仅一个事件。但是,函数调用参数中可以使用来自同一事件的多个字段。例如,以下内容有效:
$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
运算符和 nocase
运算符与引用列表结合使用,如下所示。nocase
运算符与 STRING 列表和 REGEX 列表兼容。
// Exclude events whose hostnames match substrings in my_regex_list.
not $e.principal.hostname in regex %my_regex_list
// Event hostnames must match at least 1 string in my_string_list (case insensitive).
$e.principal.hostname in %my_string_list nocase
出于性能方面的原因,检测引擎会限制引用列表的使用。
- 规则中的
in
语句数量上限(无论是否包含特殊运算符):7 - 使用
regex
运算符最多包含in
个语句:4 - 使用
cidr
运算符最多包含in
个语句:2
元部分语法
元部分由多个行组成,其中每个行定义一个键值对。键部分必须是不带英文引号的字符串,而值部分必须是带英文引号的字符串:
<key> = "<value>"
以下是有效的 meta
部分行的示例:meta:
author = "Chronicle"
severity = "HIGH"
“事件”部分语法
在 events
部分中,列出谓词以指定以下内容:
- 每个匹配项或占位符变量代表什么
- 简单二进制表达式作为条件
- 函数表达式作为条件
- 引用列表表达式作为条件
- 逻辑运算符
变量声明
对于变量声明,请使用以下语法:
<EVENT_FIELD> = <VAR>
<VAR> = <EVENT_FIELD>
这两个示例是等效的,如以下示例所示:
$e.source.hostname = $hostname
$userid = $e.principal.user.userid
此声明表明此变量表示事件变量的指定字段。当事件字段是重复字段时,匹配变量可以表示数组中的任何值。也可以将多个事件字段分配给单个匹配或占位符变量。这是一个传递性联接条件。
例如,以下方法:
$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")
引用列表表达式作为条件
您可以在“事件”部分中使用参考列表。如需了解详情,请参阅有关参考列表的部分。
逻辑运算符
您可以在 events
部分中使用逻辑 and
和逻辑 or
运算符,如以下示例所示:
$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"
默认情况下,优先级从高到低的顺序是 not
、and
、or
。
例如,"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 和实体中,某些字段被标记为重复,这表示它们是值列表或其他类型的消息。
在 YARA-L 中,重复字段中的每个元素都是单独处理的。也就是说,如果在规则中使用了重复字段,我们将针对该字段中的每个元素评估规则。这可能会导致意外行为。例如,如果规则在 events
部分同时包含 $e.principal.ip = "1.2.3.4"
和 $e.principal.ip = "5.6.7.8"
,那么即使 principal.ip
中同时具有 "1.2.3.4"
和 "5.6.7.8"
,规则也绝不会生成任何匹配项。
要将重复字段作为一个整体进行评估,您可以使用 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
运算符只能用于重复字段。此外,在将重复字段分配给 placeholder 变量或与其他事件的字段联接时,不能使用它们。
例如,any $e.principal.ip = $ip
和 any $e1.principal.ip = $e2.principal.ip
不是有效的语法。如需匹配或联接重复字段,请使用 $e.principal.ip = $ip
。重复字段的每个元素都有一个 match 变量值或联接。
使用 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"
不匹配,这意味着规则会检查是否没有 IP 地址与"192.168.12.16"
匹配。
数组索引
您可以对重复字段进行数组索引。若要访问第 n 个重复字段元素,请使用标准列表语法(元素将 0 编入索引)。出界元素会返回默认值。
$e.principal.ip[0] = "192.168.12.16"
$e.principal.ip[999] = ""
如果元素数少于 1000,则返回true
。
索引必须是非负整数字面量。包含 int 类型的值(例如设为 int 的占位符)不计算在内。数组索引不能与任何/所有类型的组合。数组索引不能与地图语法结合使用。如果字段路径包含多个重复字段,则所有重复字段都必须使用数组索引。
以下是无效语法的所有示例:
$e.intermediary.ip[0]
无效,因为中间字段是重复的字段,而我们正尝试使用数组索引。- “
$e.principal.ip[-1]
”无效,因为“-1
”不是正整数。 any $e.intermediary.ip[0]
无效,因为任何/所有类型都不能与数组索引编制结合使用。$e.additional.fields[0]["key"]
无效,因为数组索引不能与地图语法结合使用。
事件变量联接要求
规则中使用的所有事件变量必须采用以下任一方式与所有其他事件变量联接:
通过两个联接事件变量的事件字段之间的相等比较直接联接,例如:
$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><m/h/d>
其中
m/h/d
表示分钟数、小时和天数。您可以指定的最短时间为 1 分钟。
可指定的最长时间为 48 小时。
以下是有效 match
的示例:
$var1, $var2 over 5m
当规则找到匹配项时,此语句返回 $var1
和 $var2
(在 events
部分中定义)。指定时间为 5 分钟。相互间隔超过 5 分钟的事件是不相关的,因此规则会忽略这些事件。
下面是有效 match
的另一个示例:
$user over 1h
当规则找到匹配项时,此语句会返回 $user
。指定的时间窗口为 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
部分指定。然后,在每个跃点窗口中关联事件。若有跃点窗口,则无法搜索按特定顺序发生的事件(例如,e1
可在 e2
后最多 2 分钟内发生)。只要事件 e1
与事件 e2
在跃点窗口时限内,系统就会将它们关联起来。
还可以使用滑动窗口评估规则。在滑动窗口时,以指定的数据透视事件变量开始或结束时,会生成时长为 match
区段的滑动窗口。然后,在每个滑动窗口内,事件是相关的。这样,您就可以搜索按特定顺序发生的事件(例如,e1
会在 e2
的 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
是特殊名称。您可以选择性地定义使用此名称的结果;如果定义该结果,则必须是整数类型。如果填充了该名称,risk_score
将显示在来自规则检测的提醒的“企业数据洞察”视图中。
如果您未在规则的结果部分添加 $risk_score
变量,系统会设置以下默认值之一:
- 如果规则已配置为生成提醒,那么
$risk_score
会设置为 40。 - 如果规则未配置为生成提醒,则
$risk_score
设置为 15。
$risk_score
的值存储在 security_result.risk_score
UDM 字段中。
结果变量数据类型
每个结果变量可以具有不同的数据类型,该数据类型由用于计算结果变量的表达式决定。我们支持以下结果数据类型:
- 整数
- 字符串
- 整数列表
- 字符串列表
如果匹配变量位于重复字段中,且重复字段包含重复元素,则在计算结果时将重复元素视为多次。例如,这可能会导致使用 sum()
的结果出现意外值,但不会影响使用 max()
的结果。
条件逻辑
您可以使用条件逻辑来计算结果的值。条件使用以下语法模式指定:
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)
TheNN_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
和 events
部分计算整数数据类型。Chronicle 支持在计算中将加法、减法、乘法和除法作为顶级运算符。
以下代码段是 outcome
区段中的计算示例:
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)
- 如果表达式包含重复字段,则聚合针对重复字段中的所有元素、针对生成检测的所有事件进行运算
- 示例:
单事件规则(不含匹配部分)
- 用于计算结果的表达式针对生成特定检测的单个事件进行评估。
- 必须对涉及至少一个重复字段的表达式使用聚合函数
- 示例:
$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
,因此聚合函数将对多个事件进行运算。假设以下事件生成了一项检测:
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
部分中关联的事件变量。来自不同事件变量的两个事件字段相等时会产生相关性。
您可以使用 YARA-L 2.0 概览中的结果部分找到示例。 如需详细了解如何通过结果部分对检测进行重复数据删除,请参阅创建情境感知分析。
条件部分语法
在 condition
部分,您可以执行以下操作:
- 针对
events
部分中定义的事件和占位符指定匹配条件。如需了解详情,请参阅事件和占位符条件部分。 - (可选)使用
and
关键字,使用outcome
部分中定义的结果变量来指定匹配条件。如需了解详情,请参阅下方的结果条件。
以下条件模式有效:
condition:
<event/placeholder conditionals>
condition:
<event/placeholder conditionals> and <outcome conditionals>
事件和占位符条件
在此处列出事件和占位符变量的条件条件,并通过关键字 and
或 or
进行联接。
以下条件即为边界条件。它们强制关联事件变量的存在,这意味着任何检测都必须显示相应事件的发生实例。
$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
您可以用实体加入活动,然后检查是否有活动没有。以下伪代码示例结合了 events
部分中的事件字段 $u.field
和实体字段 $e.graph.field
,还检查了 condition
部分 !$u and $e
中缺少该事件。
events:
$u.field = "value" // $u is an event
$e.graph.field = "value" // $e is an entity
// ...other sections of the rule...
condition:
!$u and $e
您无法检查实体是否不存在。以上面的示例来说,以下用于检查实体是否存在的声明是无效的:$u and !$e
。
在以下示例中,变量上的特殊字符 #
(事件变量或占位符变量)表示该变量的不同事件或值的数量。
$e and #port > 50 or #event1 > 2 or #event2 > 1 or #event3 > 0
如果来自 $event1
的两个不同事件以及来自 $event2
的不同事件为零,则以下不存在的示例也有效且评估结果为 true。
#event1 > 2 and !$event2
哪些类型的事件或占位符变量可以具有非绑定条件。变量必须是下列其中一项:
- 包含 2 个或更多 UDM 事件变量的规则中的 UDM 事件变量。
- 与至少 1 个 UDM 事件变量相关联的占位符变量,没有非绑定条件。
以下是无效谓词的示例:
$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
开头。
根据结果变量的类型以不同方式指定结果条件:
integer:与使用运算符
=, >, >=, <, <=, !=
的整数字面量进行比较,例如:$risk_score > 10
字符串:使用
=
或!=
与字符串字面量进行比较,例如:$severity = "HIGH"
整数或数组列表:使用
arrays.contains
函数指定条件,例如:arrays.contains($event_ids, "id_1234")
规则分类
在具有匹配部分的规则中指定结果条件有条件意味着相应规则将归类为规则配额的多事件规则。如需详细了解单个事件分类和多个事件分类,请参阅单个事件规则和多个事件规则。
数量 (#) 字符
#
字符是 condition
部分中的特殊字符。如果在任何事件或占位符变量名称之前使用,它表示满足所有 events
部分条件的不同事件或值的数量。
值 ($) 字符
$
字符是 condition
部分的另一个特殊字符。在使用任何结果变量名称之前使用该参数,则表示相应结果的价值。
如果将其用于任何事件或占位符变量名称(例如 $event
),则它是 #event > 0
的简写形式。
选项部分语法
在 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"