Sigma 规则是目前最通用的厂商中立检测规则格式,一条 YAML 可以在 Elastic、Splunk、Wazuh 之间转换复用。但多数团队把它当成”多写几条规则”的工具,结果规则库越堆越大,日均告警从几十条涨到几千条,真正的攻击告警被淹在误报里。本文从一条可用的规则写起,讲清楚怎么把告警量压到值班人员能处理的水平。
适用场景
三类情况适合立刻动手改:日志接入完成后告警量失控,值班人员开始习惯性忽略推送;同一攻击行为在多个平台上各写一套规则,版本无法对齐;供应商规则包直接导入后误报率过高,没人敢开。
反过来,如果日志还没做归一化,字段名混乱、时间戳时区不一致、关键事件缺失,那先补日志质量再谈规则。规则写得再准,日志里没有这个字段也不会命中,这是很多团队走了半年弯路才承认的事实。
前置条件
- 一台可联网的分析机,Python 3.9 以上版本;
- 日志后端已就绪,能检索进程创建、登录、文件写入三类事件;
- 至少 7 天历史日志用于回放,没有历史数据就无法量化误报率;
- 清楚自己的字段映射关系,规则里写
Image,后端可能叫process.executable。
原理说明
Sigma 的核心是检测逻辑与查询语法分离。规则只描述”什么条件算可疑”,字段采用统一的抽象命名,转换时由 backend 与 pipeline 负责翻译成目标平台的查询语句。这带来两个直接好处:规则可跨平台复用;误报治理集中在规则层完成,而不是散落在各平台的告警配置里各自为政。
告警降噪有三个抓手。字段精确化:用 Image|endswith 限定可执行文件路径,而不是对整条命令行做宽泛的 contains,命中面能缩小一个量级。条件收敛:一条规则只表达一个行为链,用 and 串联必要条件,避免把多个可疑特征用 or 堆在一起放大噪声。误报显式化:把已知的正常行为写成独立的 filter 规则,让”这条规则在什么情况下应该静音”成为可审计、可复审的配置,而不是某个人在告警平台上点了个忽略。
规则层解决”该不该报”,聚合层解决”报几次”。同一主机五分钟内相同规则只推一条、不同规则命中同一主机合并为一个事件,这两步能再砍掉一半以上的重复推送。两层都做完,才谈得上告警可用。
操作步骤
步骤一:安装转换工具链
当前推荐 sigma-cli,旧的 sigmac 已停止维护。按需安装目标后端即可,不必全装。
python3 -m pip install --upgrade sigma-cli
python3 -m pip install pysigma-backend-elasticsearch pysigma-backend-splunk
sigma version
sigma list targets
sigma list targets 会列出当前可用的转换目标,能看到 lucene、splunk、elasticsearch 即安装成功。
步骤二:编写第一条规则
以下规则检测”下载器拉取远程内容后立即管道给 shell 执行”这一行为链,对应 ATT&CK 的 T1059.004。注意两个选择器是 and 关系,缺一不报。
title: Curl Or Wget Pipe To Shell
id: 3f9a1c62-5b47-4d0e-9a11-7c2e8b4f6d31
status: experimental
description: 检测 curl 或 wget 拉取远程内容后直接管道给 shell 执行
references:
- https://attack.mitre.org/techniques/T1059/004/
author: wafai.cn
date: 2026/10/24
logsource:
category: process_creation
product: linux
detection:
selection_download:
Image|endswith:
- '/curl'
- '/wget'
CommandLine|contains:
- 'http://'
- 'https://'
selection_pipe:
CommandLine|contains:
- '| bash'
- '|bash'
- '| sh'
- '|sh'
condition: selection_download and selection_pipe
falsepositives:
- 容器镜像构建脚本
- 经审批的自动化部署任务
level: high
tags:
- attack.execution
- attack.t1059.004
规则写完先做语法校验,再转换。校验能提前发现字段拼写、缩进层级这类低级错误,避免把坏规则推到生产环境。
sigma check rules/linux/curl_pipe_shell.yml
sigma convert -t lucene rules/linux/curl_pipe_shell.yml
sigma convert -t splunk rules/linux/curl_pipe_shell.yml
lucene 输出可直接粘进 Kibana 的搜索框,splunk 输出是完整 SPL。注意转换结果的字段名必须和索引里的实际字段对齐,不一致时用 pipeline 做映射,不要在规则里逐个改名。
步骤三:把误报写成过滤规则
上线一周后大概率会看到某台构建机天天命中。此时不要改主规则的 condition,新建一条过滤规则,用父进程做收敛。父进程是判断”谁在调用它”最有效的字段,比匹配命令行关键词可靠得多,因为命令行可以改名,父进程路径不容易伪造。
title: Filter - Build Pipeline Curl Pipe Shell
id: b71d4e08-2c95-4a3f-8e60-1d5a9f3c7b24
status: test
description: 排除构建流水线内部的 curl 管道执行
logsource:
category: process_creation
product: linux
detection:
selection:
ParentImage|endswith:
- '/gitlab-runner'
- '/buildkitd'
CommandLine|contains:
- 'install.sh'
- 'deploy.sh'
condition: selection
level: informational
转换后把输出作为排除条件挂到后端告警查询上,而不是在 Sigma 主规则里写 and not filter。原因很实际:排除条件会随时间变化,分开维护才能各自回滚,也不会因为一条过滤规则写错就废掉整条检测逻辑。
步骤四:用历史日志回放评估误报率
规则上线前必须回放。取七天日志,统计每条规则的原始命中数,再扣除过滤规则命中数,得到实际告警量。单条规则日均超过五十条且人工确认无真实攻击,就该继续收敛字段。
sigma convert -t lucene rules/linux/curl_pipe_shell.yml > /tmp/q1.txt
sigma convert -t lucene rules/filters/build_pipeline_filter.yml > /tmp/q2.txt
# 在 Kibana 依次执行 q1、q2,记录命中数
# 用 _count 接口做程序化统计,便于改动前后对比
curl -s -u elastic:PASS 'https://127.0.0.1:9200/logs-*/_count' \
-H 'Content-Type: application/json' \
-d '{"query":{"bool":{"must":[{"query_string":{"query":"process.name:curl AND process.command_line:*bash*"}}],"must_not":[{"query_string":{"query":"process.parent.name:gitlab-runner"}}]}}}'
把 _count 的返回值记录下来,改动规则前后各跑一次,降噪效果就有量化依据,而不是凭感觉说”好像少了很多”。
步骤五:配置聚合抑制
规则层收敛后仍会有重复推送。在告警规则上设置聚合窗口:按 host.name 分组,五分钟内同一规则只触发一次,命中次数写入事件字段。ELK 用规则设置里的分组字段与回看窗口,Wazuh 侧对应 frequency 与 timeframe。
<rule id="100201" level="10" frequency="3" timeframe="300">
<if_matched_sid>100200</if_matched_sid>
<description>同一主机 5 分钟内多次下载并执行远程脚本</description>
<group>attack.execution,</group>
</rule>
frequency 表示匹配次数,timeframe 单位是秒。把单次命中降为低等级记录、多次命中升级为告警,是压缩噪声最有效的一步,也顺带解决了”扫一下就打一次电话”的排班难题。
配置验证
- 语法验证:
sigma check全部通过,无 warning 遗留。 - 语义验证:转换出的查询在后端手工执行,用一条已知恶意样本的命令行做正向测试必须命中,用构建机的正常命令行做反向测试必须不命中。
- 量级验证:回放七天日志,单规则日均告警落在可处理区间,经验值二十条以内。
- 闭环验证:随机抽十条告警人工判定,误报率低于两成视为达标,否则回到步骤三继续加过滤条件。
常见问题
Q1:转换出来的查询字段名和索引里的不一致怎么办?
这是字段映射问题,不是规则问题。先确认后端的字段规范,Elastic 走 ECS、Splunk 走 CIM,各有约定;再安装对应 pipeline,用 sigma convert -p 指定。若只是个别字段差异,写一个自定义 pipeline 做映射,比在每条规则里硬改字段名可持续得多。
Q2:规则越多是不是越安全?
不是。规则数量与检测能力不是线性关系,覆盖度靠的是行为链完整度和日志质量。日志缺失的规则写得再准也不会命中;反过来规则过多推高误报,最终导致值班人员习惯性忽略告警,这比没有告警更危险。建议按 ATT&CK 技术点做覆盖度盘点,缺口优先补日志源而不是继续堆规则。
Q3:过滤规则会不会把真实攻击一起过滤掉?
会,所以过滤条件必须限定得足够窄。只用父进程路径排除相对安全;一旦叠加”命令行包含 install”这种宽泛条件,攻击者把脚本改名为 install.sh 就能绕过。过滤规则里写清放行理由和复核日期,每季度复审一次,避免沉淀成永久性的盲区。
总结
Sigma 的价值不在规则数量,而在于把检测逻辑变成可评审、可转换、可回放的资产。落地顺序建议:先统一字段,再写少量行为链规则,然后用历史日志回放量化误报,最后靠过滤规则与聚合抑制把量压下来。判断一条规则是否合格的标准只有一个——它命中时,值班人员愿不愿意立刻打开看。