利用Splunk Lookups加速检测工程:简化字符串检测规则构建

本文介绍了如何利用Splunk的Lookup功能,通过将检测字符串存入CSV表格,快速创建和管理基于字符串的检测规则,以减少检测规则开发和部署的时间。文章详细阐述了Sigma规则的转换方法、三种具体实施的SPL搜索语法及其优缺点。

概述

这篇文章介绍了我如何发现可以利用 Splunk 中的 Lookup(查找表)功能,来显著减少为基于简单字符串的检测规则(例如“如果看到 dump /service:krbtgt 则触发”)实现新检测所需的时间。

随着检测即代码的部署,或许这会减少对此类方法的需求。但根据我目前的经验,部署一个本质上只是字符串搜索的新规则,往往耗时过长。

背景

新规则开发的常见路径是:阅读一份报告,识别一种特定的战术、技术和程序实现,然后创建或寻找一个规则,例如来自某个地方的 Sigma 规则(像 https://sigmasearchengine.com、https://detection.fyi/ 或直接来自 https://github.com/SigmaHQ/sigma)。

即使在采用检测即代码的环境中,这仍然可能是一个漫长的过程,并且也可能意味着我们会留下成百上千个检测规则,每个规则都有自己的管理开销,而它们本质上可能只是字符串指标。

解决方案——使用 Lookup!

在 Splunk 中,一个 Lookup 本质上就是一个 CSV 表格,可以存储我们希望查找的数据。

简而言之,Splunk 中现有的 IOC 威胁匹配就使用了 Lookup 或 KV 存储。我们只是扩展了这个概念,使其包含像 *mimikatz* 这样的字符串片段,并附加我们希望在哪个字段中看到它们——例如 command(命令行)字段。

Sigma 规则示例

以这个查找命令行参数的 Sigma 规则为例:

所有感兴趣的内容都只是出现在 CommandLine 中的字符串。我们只需提取像这样的简单 Sigma 规则的主题,并将其转换为 CSV 结构。

对于上面的例子,我们可以看到它会查找多个命令行片段中的任何一个。这确实说明了基于 Lookup 方法的一个局限性,即如果我们不希望承担必须分割分隔字段的开销,那么所有这些片段都必须放在单独的行上,这对于某些规则来说可能相当“杂乱”。

示例 Lookup 文件

下面的示例 Lookup 文件存储了我们最感兴趣的信息,例如:

  • l_field = 我们希望在其中查找值的字段
  • l_value = 我们希望查找的值(注意其周围有通配符)
  • l_description / l_name = 此条目的名称或描述
  • l_references = 任何用于调查的有用参考
  • l_severity = 此条目可能触发的严重性(可作为风险因子用于 RBA 计算)

示例 Lookup

1
2
l_field,l_value,l_description,l_severity
command,*mimikatz*,Mimikatz tool detection,high

在 Splunk 搜索中使用它

我们可以通过几种方式在 Splunk 搜索中使用这个 Lookup,每种方式的效率各不相同。其核心思路是能够指向一个索引,告诉它我们关心哪些字段,然后进行查找。

重要步骤:

  1. 确保为你的 Lookup 定义了一个 Lookup 定义。
  2. 确保已在 Lookup 定义中将你搜索的字段启为通配符字段。
  3. 所有要搜索的条目都应保存为带有周围通配符 * 的格式。

示例 SPL:方法 1 —— 先用 inputlookup 子搜索,再配合 tstats

1
2
3
4
5
6
| tstats summariesonly=true c from datamodel="Endpoint.Processes"
where `rule_tuning_macro` [|inputlookup string_indicator_lookup | rename l_value as Processes.process | fields Processes.process] by _time Processes.process Processes.user host
| rename Processes.* as *
| eval lookup_field="command"
| eval lookup_value=process
| lookup string_indicator_lookup l_field as lookup_field l_value as lookup_value

在这里,我们使用了一个加速的数据模型,并使用了一个内部搜索,即 inputlookup,在这里我们重命名字段,然后只将字符串输出到我们关心的字段中(此处是 processes.process)。我们可以针对这个较小的数据集进行搜索,并直接使用 Lookup 来丰富任何发现。

与编写规则的良好实践一样,我们包含了一个调优宏,其中包含了我们的误报等信息,包括使用原始数据中存在的其他字段进行调优。

缺点: 随着 Lookup 文件的增长,由于 inputlookup 命令在 Splunk 索引器上的工作方式,这可能会相当慢。搜索需要定期运行,并且时间窗口要小。

示例 SPL:方法 2 —— 用 tstats 拉取所有数据,然后进行 Lookup

1
2
3
4
5
| tstats summariesonly=true c from datamodel="Endpoint.Processes" where `rule_tuning_macro` by _time Processes.process Processes.user host
| rename Processes.* as *
| eval lookup_field="command"
| eval lookup_value=process
| lookup string_indicator_lookup l_field as lookup_field l_value as lookup_value

在这个例子中,我们查看加速的 Endpoint 数据模型中的所有命令(Processes.process),然后对每个命令进行 Lookup,从而允许我们看到 Lookup 中的所有字段,如描述或参考信息。同样,我们使用了一个调优宏。

缺点: 这可能相当慢,因为它首先将所有命令拉入内存,所以请在较短的时间范围内运行它,不过 Lookup 文件的增长对其影响可能较小。

示例 SPL:方法 3 —— 在非数据模型数据上,结合使用 inputlookup 和 lookup

1
2
3
4
5
{{YOUR DATA}}
| eval lookup_field="command"
| eval lookup_value=command
| search[|inputlookup string_indicator_lookup | eval search=l_value |fields search | format ]
| lookup string_indicator_lookup l_field as lookup_field l_value as lookup_value

这里我们没有使用加速的数据模型,但为了加速,我们使用了两次处理。第一次 inputlookup 在原始数据中寻找我们关心的字符串——此时它可能出现在任何字段中,但这应该会极大地缩减后续 Lookup 需要处理的数据集。然后我们紧接着进行一次 Lookup,确保匹配发生在正确的字段上,并用 Lookup 中的上下文信息丰富匹配结果。

进一步探索

这些搜索相当基础,因此有一些想法我想探索:

  • 使用分隔符减少重复/多行 —— 我认为涉及的解析可能会严重影响搜索时间,尤其是当我们无法进行简单的 Lookup 时。不过我想进一步研究一下。
  • 附加字段,例如 parent_process —— 仅当两者都满足时才匹配。
  • Lookup 中的逻辑 —— 在 Lookup 中存储 Sigma 逻辑(如 ‘ANY’、‘ALL’)并在 SPL 搜索中利用它。
  • 在摄入时/实时执行此操作 —— 我希望在日志进入 Splunk 时就能进行简单的模式匹配,有点像 IDS。我想看看在日志摄入时进行这样的 Lookup 可行性如何。

结论

必须承认,可能存在一个临界点,届时使用 Lookup 进行这些操作可能还不如直接使用多个规则来得高效。然而,我们的目标是极大地减少复杂性,缩短从阅读报告时想到“我们或许可以找找那个东西”,到真正有一个规则在寻找它之间的时间。

如果任何人有什么反馈,请告诉我!(我确实喜欢重新发明完美的轮子,所以如果有更好的方法,请告诉我!)

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计