3. 检测规则语言与表达基础
在 SIEM 检测规则开发工程中,规则语言是检测逻辑的“载体”。同一类攻击行为,用不同语言表达时,其结构、可移植性、时间窗口能力和查询性能差异很大。理解这些差异,是后续编写、评审和迁移规则的前提。
一、五类规则语言的定位差异
Sigma 是厂商中立的检测规则描述格式,通常以 YAML 编写,核心价值在于“一次编写、多平台转换”。它本身不是查询语言,而是通过后端转换器(如 pySigma)编译为 Splunk SPL、Elastic KQL、Microsoft Sentinel KQL 等目标查询。YARA-L 是 Google Chronicle 的检测语言,语法接近类 C 表达式,强调事件流上的多事件关联与时间窗口。SPL 是 Splunk 的搜索处理语言,以管道 | 串联命令,擅长在海量事件上做统计与聚合。KQL 是 Microsoft 生态(Sentinel、Defender、Azure Data Explorer)的查询语言,表与操作符风格接近 SQL,支持 join、summarize、make_set 等。AQL 是 QRadar 的 Ariel 查询语言,面向 AQL 数据模型,常用于自定义规则与离线检索。
选择语言时,关键不是“哪种更强”,而是“目标平台支持什么、团队维护成本如何”。若组织同时使用多个 SIEM,Sigma 作为源格式、再编译到各平台,是降低重复劳动的有效路径;若检测强依赖多事件关联与长窗口,YARA-L 或 KQL 的表达更自然。
二、规则结构的通用骨架
无论哪种语言,一条可维护的检测规则通常包含四层:元数据、数据源与字段映射、条件逻辑、时间窗口与聚合。元数据回答“这条规则是什么、谁维护、严重性如何”;数据源与字段映射回答“从哪张表/索引取数、字段叫什么”;条件逻辑回答“满足什么条件算命中”;时间窗口回答“在多长时间内、以什么粒度聚合”。
以 Sigma 为例,其结构通常如下:
title: 可疑 PowerShell 下载执行
id: 8f2a1c3e-4b5d-4e6f-9a7b-0c1d2e3f4a5b
status: experimental
description: 检测 PowerShell 通过 IEX 下载并执行远程脚本
author: SOC Detection Team
date: 2025/01/15
logsource:
product: windows
category: process_creation
detection:
selection:
Image|endswith: '\powershell.exe'
CommandLine|contains:
- 'IEX'
- 'Invoke-Expression'
- 'DownloadString'
condition: selection
falsepositives:
- 管理员合法运维脚本
level: high
tags:
- attack.execution
- attack.t1059.001
其中 logsource 声明数据来源,detection 下的 selection 是字段匹配块,condition 决定逻辑组合方式,level 与 tags 属于元数据。转换到 SPL 后,可能变成:
index=windows EventCode=4688
| where like(CommandLine, "%IEX%") OR like(CommandLine, "%Invoke-Expression%")
| where like(Image, "%\\powershell.exe")
| table _time, ComputerName, User, CommandLine
可以看到,Sigma 的字段匹配被翻译为 SPL 的 where 与 like,而元数据中的 level 通常不会进入查询本身,而是由平台侧规则配置承载。
三、条件逻辑:从单事件到多事件
条件逻辑的复杂度决定了规则语言的选择。单事件规则只判断一条日志是否满足字段组合,例如“进程名是 powershell.exe 且命令行包含 DownloadString”。这类规则在 Sigma、SPL、KQL 中都很容易表达。
多事件规则需要关联同一实体在多个事件上的行为,例如“同一用户在 5 分钟内先出现多次登录失败,随后登录成功”。这类规则在 YARA-L 中可用事件变量与时间窗口直接表达:
rule brute_force_then_success {
meta:
author = "SOC"
severity = "HIGH"
events:
$fail.metadata.event_type = "USER_LOGIN"
$fail.security_result.action = "FAIL"
$success.metadata.event_type = "USER_LOGIN"
$success.security_result.action = "SUCCESS"
$fail.principal.user.userid = $success.principal.user.userid
match:
$userid over 5m
condition:
#fail >= 5 and $success
}
这里 match 指定按用户聚合,over 5m 定义时间窗口,#fail >= 5 表示失败事件计数阈值。KQL 中类似逻辑通常用 summarize 配合 bin() 实现:
SigninLogs
| where TimeGenerated > ago(1h)
| summarize FailCount = countif(ResultType != "0"),
SuccessCount = countif(ResultType == "0")
by UserPrincipalName, bin(TimeGenerated, 5m)
| where FailCount >= 5 and SuccessCount >= 1
SPL 则常用 stats 与 transaction:
index=auth action=login
| stats count(eval(status="fail")) as fails,
count(eval(status="success")) as success
by user
| where fails >= 5 and success >= 1
三种写法都能表达“5 次失败后成功”,但 YARA-L 把时间窗口和实体匹配写进了语言原语,SPL 与 KQL 则依赖聚合命令和分组字段。这意味着迁移规则时,时间窗口语义必须逐条核对,不能只做语法替换。
四、时间窗口与聚合粒度
时间窗口是检测规则中最容易被忽视、也最容易产生误报的部分。窗口过短会漏检慢速攻击,窗口过长会引入大量无关事件。常见窗口类型包括:滑动窗口(如“过去 5 分钟”)、固定窗口(如“每 5 分钟桶”)、会话窗口(如“同一会话内”)。
在 Sigma 中,时间窗口通常不直接写在规则主体里,而是通过 condition 的聚合表达式或后端转换时的 timeframe 参数表达。例如:
detection:
selection:
EventID: 4625
condition: selection | count() by TargetUserName > 5
timeframe: 5m
这段规则表示“5 分钟内同一账户失败登录超过 5 次”。转换到不同后端时,timeframe 会被翻译为对应平台的窗口语法。若目标平台不支持该语义,转换器可能报错或降级为无窗口查询,因此规则评审时必须确认转换结果。
五、元数据与规则治理
元数据不是装饰,而是规则治理的基础。一条规则至少应包含:唯一 ID、标题、描述、作者、创建/修改日期、严重性、置信度、数据源、ATT&CK 映射、误报说明、参考链接。缺少唯一 ID 会导致规则在迁移和版本管理中无法追踪;缺少误报说明会让后续维护者不敢调整阈值。
不同语言的元数据字段名不同,但语义可以对齐。例如 Sigma 的 level 对应 YARA-L 的 severity,Sigma 的 tags 对应 ATT&CK 技术编号。在跨语言迁移时,建议先建立一张元数据映射表,再逐条转换检测逻辑,避免“逻辑迁了、元数据丢了”。
六、规则评审的检查要点
在团队协作中,规则评审应覆盖以下问题:规则是否有唯一 ID 和明确作者;数据源字段是否与目标平台实际字段一致;条件逻辑是否存在大小写、通配符、转义问题;时间窗口是否与业务节奏匹配;阈值是否有历史数据支撑;误报说明是否具体;ATT&CK 映射是否准确;转换到目标平台后是否经过实际查询验证。
其中“字段一致性”是最常见的失败点。Sigma 中的 Image 在 Splunk 中可能是 Image 或 process_name,在 Sentinel 中可能是 FolderPath 或 NewProcessName。字段映射错误不会导致语法报错,但会让规则永远不命中。因此,规则上线前应在目标平台用已知样本事件做一次命中验证,确认查询返回预期结果。
掌握规则语言的结构与差异后,下一步才是针对具体攻击技术编写检测逻辑。语言是工具,检测目标、数据质量和验证方法才是决定规则有效性的根本。