使用Anomalize进行异常检测与威胁狩猎的完整指南

本文详细介绍了如何使用R语言的anomalize包进行安全事件日志的异常检测,包含STL和Twitter两种时间序列分解方法,以及IQR和GESD异常检测算法,帮助蓝队应对大规模安全数据分析挑战。

工具集第133期:使用Anomalize进行异常检测与威胁狩猎

在十月和十一月的工具集文章中,我在"R语言调查员深度功能"的前提下重新定义了DFIR,当时我发现这只是"冰山一角"。为此,我想通过额外的发现和机会重新探讨这个概念。实际上,这是在原始且最重要的DFIR(数字取证/事件响应)通用实践中的DFIR(R语言调查员深度功能)案例。

正如之前讨论的,我们这些从事DFIR实践和蓝队工作的人都被数据和规模所淹没。成功确实需要算法方法。如果你还没有投入这方面,我有一个关于使用anomalize进行整洁异常检测的即时应用案例研究。

首先,让我完全归功于后续工作的贡献者。我讨论和提供的一切都直接源自Business Science(@bizScienc),特别是Matt Dancho(@mdancho84)。当客户要求Business Science构建适合他们需求的开源异常检测算法时,他创建了anomalize,这是"一个基于时间的整洁异常检测算法(构建在tibbletime之上),可从一个时间序列扩展到多个时间序列"。当他的博客文章通过R-Bloggers进入我的视野时,我认为他的回应非常出色,它在我的浏览器中作为一个打开的标签页存在了一个多月,直到催生了这篇工具集文章。请将Matt的文章视为此过程第一步的必读内容。

关键要点:在大规模每日或每周数据中检测时间序列异常。异常表示异常事件。

现在与我一起将上下文转向特定安全事件和事故,因为它们涉及安全监控、事件响应和威胁狩猎。在我2017年11月的文章中,回想一下我讨论了使用Holt-Winters方法的时间序列回归,重点关注季节性和趋势。不幸的是,我无法分享我们如何应用TSR的代码,但指出了替代方法,包括使用Loess的季节和趋势分解(STL):

  • 处理任何类型的季节性~可以随时间变化
  • 趋势周期的平滑度也可以由用户控制
  • 对异常值具有鲁棒性

现在,Matt创建了一种立即应用STL方法以及Twitter方法(参考页面)的手段,作为他的time_decompose()函数的一部分,这是anomalize包特有的三个函数之一。除了将时间序列分离为季节性、趋势和剩余组件的time_decompose()外,anomalize还包括:

  • anomalize():对剩余组件应用异常检测方法
  • time_recompose():计算将"正常"数据与异常分离的限制

Matt最终着手构建Twitter的AnomalyDetection包的可扩展适配,以解决客户在处理不是一個而是数千个需要分析极端事件的时间序列时的挑战。

我想针对真实的安全数据场景运行anomalize,因此我回到了原始DFIR文章中的数据集,其中我使用了在给定服务器集上每个用户每天的4624事件ID计数。正如最初使用的那样,我只代表了一个设备和用户的具体结果,但这里是anomalize的美妙之处。我们可以在多个时间序列(多个系统/用户)上快速获得结果。这个前提只是将时间序列分析和季节性应用于安全数据的众多场景之一。

我首先尝试将log.csv中的日志数据直接写入anomalize.R脚本,使用logs = read_csv("log.csv")到tibble中(准备好你的tibbles笑话),但这没有被准确解析,特别是时间属性。为了纠正这一点,我从Matt的Github获取了tidyverse_cran_downloads.R,并修改如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 从Anomalize项目创建,Matt Dancho
# https://github.com/business-science/anomalize
library(dplyr)
library(tibbletime)
setwd("C:/coding/R/anomalize/")
logs <- read_csv("log.csv")
security_access_logs <- logs %>%
  group_by(server) %>%
  as_tbl_time(date)
security_access_logs

这得益于tibbletime包大大帮助,这是"一个允许创建时间感知tibbles的扩展。一些直接优势包括:能够对tibbles执行基于时间的子设置,快速按时间段总结和聚合结果。“猜猜看,Matt也编写了tibbletoo。:-)

然后我按照Matt在Business Science上发布的序列,但将我的日志定义为Security_Access_Logs_Function.R中的函数。接下来,我将给你代码片段,如从Matt的示例修订的那样,然后是它们各自处理我的事件ID 4624每日计数日志的结果。

首先,让我们总结四个月内三个服务器上的每日登录计数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 从Anomalize项目创建,Matt Dancho
# https://github.com/business-science/anomalize
library(tidyverse)
library(anomalize)
security_access_logs %>%
  ggplot(aes(date, count)) +
  geom_point(color = "#2c3e50", alpha = 0.25) +
  facet_wrap(~ server, scale = "free_y", ncol = 3) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) +
  labs(title = "服务器登录计数",
       subtitle = "来自安全事件日志的数据,事件ID 4624")

接下来,让我们使用Matt的三个主要函数time_decompose()anomalize()time_recompose(),以及可视化函数plot_anomalies(),确定哪些每日下载登录是异常的,跨越四个月内相同的三个服务器。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 从Anomalize项目创建,Matt Dancho
# https://github.com/business-science/anomalize
security_access_logs %>%
  # 数据操作/异常检测
  time_decompose(count, method = "stl") %>%
  anomalize(remainder, method = "iqr") %>%
  time_recompose() %>%
  # 异常可视化
  plot_anomalies(time_recomposed = TRUE, ncol = 3, alpha_dots = 0.25) +
  labs(title = "安全事件日志异常", subtitle = "STL + IQR方法")

遵循Matt使用Twitter的AnomalyDetection包的方法,结合time_decompose(method = "twitter")anomalize(method = "gesd"),同时调整trend = "4 months"来调整中位数跨度,我们将只关注SERVER-549521。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 从Anomalize项目创建,Matt Dancho
# https://github.com/business-science/anomalize
# 仅获取SERVER549521访问
SERVER549521 <- security_access_logs %>%
  filter(server == "SERVER-549521") %>% 
  ungroup()
# 异常检测!!
SERVER549521 %>%
  # Twitter + GESD
  time_decompose(count, method = "twitter", trend = "4 months") %>%
  anomalize(remainder, method = "gesd") %>%
  time_recompose() %>%
  # 异常可视化
  plot_anomalies(time_recomposed = TRUE) +
  labs(title = "SERVER-549521异常", subtitle = "Twitter + GESD方法")

我们可以比较Twitter(time_decompose)和GESD(anomalize)方法与STL(time_decompose)和IQR(anomalize)方法,它们使用不同的分解和异常检测方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 从Anomalize项目创建,Matt Dancho
# https://github.com/business-science/anomalize
SERVER549521 %>%
  # STL + IQR异常检测
  time_decompose(count, method = "stl", trend = "4 months") %>%
  anomalize(remainder, method = "iqr") %>%
  time_recompose() %>%
  # 异常可视化
  plot_anomalies(time_recomposed = TRUE) +
  labs(title = "SERVER-549521异常", subtitle = "STL + IQR方法")

最后,让我们使用Matt的plot_anomaly_decomposition()来可视化算法如何检测SERVER-549521剩余中的异常的内部工作。

1
2
3
4
5
6
7
8
9
# 从Anomalize项目创建,Matt Dancho
# https://github.com/business-science/anomalize
security_access_logs %>%
  filter(server == "SERVER549521") %>%
  ungroup() %>%
  time_decompose(count) %>%
  anomalize(remainder) %>%
  plot_anomaly_decomposition() +
  labs(title = "异常化SERVER-549521下载的分解")

结果是一个四部分的可视化,包括观察值、季节、趋势和剩余。

我真的很期待在更大规模、更广泛的事件日志数据集上使用这些方法。我坚定地断言,蓝队在对抗自动化对手策略和纯粹规模问题方面已经远远落后,所以…很多…数据。只有通过像Matt的anomalize这样的策略及其同类,防御者才能有望成功。务必观看Matt关于anomalize的YouTube视频,Business Science正在构建一系列视频,所以请密切关注那里和他们的GitHub,以获取更多我们可以应用蓝队/防御者背景的优秀工作。

所有代码片段都在我的GitHubGist这里,示例日志文件、单个R脚本和Jupyter Notebook都可以在我的GitHub上的toolsmith_r中找到。我希望你发现anomalize像我一样令人兴奋和有用,Matt做得很好,期待看到Business Science的下一步。

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