你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
Splunk 检测规则是安全信息和事件管理 (SIEM) 组件,与 Microsoft Sentinel 中的分析规则进行比较。 本文介绍识别、比较和迁移到Microsoft Sentinel的概念。 最佳方法是从 SIEM 迁移体验开始,该体验可识别要自动转换为的现成 (OOTB) 分析规则。
如果要迁移 Splunk 可观测性部署,请详细了解如何从 Splunk 迁移到 Azure Monitor 日志。
审核规则
Microsoft Sentinel使用机器学习分析来创建高保真且可操作的事件。 某些现有的 Splunk 检测在Microsoft Sentinel中可能是冗余的,因此不要盲目迁移它们。 在识别现有检测规则时,请查看这些注意事项。
- 考虑到业务优先级和效率,请确保选择符合规则迁移的用例。
- 检查是否了解Microsoft Sentinel规则类型。
- 检查是否了解 规则术语。
- 查看过去 6-12 个月没有警报的过时规则,并确定它们是否仍然相关。
- 消除经常忽略的低级别威胁或警报。
- 确认连接的数据源并查看数据连接方法。 Microsoft Sentinel Analytics 要求在启用规则之前,Log Analytics 工作区中存在数据类型。 重新访问数据收集对话,以确保数据深度和广度跨计划检测的用例。 然后,使用 SIEM 迁移体验 来确保正确映射数据源。
迁移规则
确定要迁移的 Splunk 检测后,请查看迁移过程的以下注意事项:
- 将Microsoft Sentinel的 OOTB 分析规则的现有功能与当前用例进行比较。 使用 SIEM 迁移体验 查看哪些 Splunk 检测会自动转换为 OOTB 模板。
- 转换不符合 OOTB 分析规则的检测。 自动转换 Splunk 检测的最佳方法是使用 SIEM 迁移体验。
- 通过探索 SOC 主要威胁检测市场等社区资源,发现适用于你的用例的更多算法。
- 如果内置规则不可用或未自动翻译,请手动转换检测。 创建新的 KQL 查询并查看 规则映射。
有关详细信息,请参阅 迁移检测规则的最佳做法。
规则迁移步骤
验证是否为要迁移的每个规则都安装了测试系统。
为迁移的规则准备验证过程,包括完整的测试方案和脚本。
确保团队具有有用的资源 来测试迁移的规则。
确认已连接所需的数据源, 并查看数据连接方法。
验证检测在 Microsoft Sentinel 中是否可用作 OOTB 模板:
使用 SIEM 迁移体验 自动翻译和安装 OOTB 模板。
有关详细信息,请参阅 使用 SIEM 迁移体验。
如果你的用例未反映在检测中,请使用 OOTB 规则模板为自己的工作区创建规则。
在 Microsoft Sentinel 中,转到内容中心。
筛选分析规则模板的内容类型。
查找和 安装/更新 每个相应的内容中心解决方案或独立分析规则模板。
有关详细信息,请参阅 现装检测威胁。
如果有Microsoft Sentinel OOTB 规则未涵盖的检测,请先尝试使用 SIEM 迁移体验进行自动转换。
如果 OOTB 规则和 SIEM 迁移都没有完全转换检测,请手动创建规则。 在这种情况下,请使用以下步骤创建规则:
确定要在规则中使用的数据源。 通过在数据源和数据表之间创建映射表,确定要查询的Microsoft Sentinel表。
标识数据中要在规则中使用的任何属性、字段或实体。
确定规则条件和逻辑。 在此阶段,请考虑查找规则模板作为有关如何构造 KQL 查询的示例。
考虑筛选器、相关规则、活动列表、引用集、监视列表、检测异常、聚合等。 可以使用旧版 SIEM 提供的引用 来了解如何最好地映射查询语法。
确定触发条件和规则操作,然后构造并查看 KQL 查询。 查看查询时,请考虑 KQL 优化指南资源。
使用每个相关用例测试规则。 如果它未提供预期结果,请查看并编辑 KQL 并再次测试。
如果满意,请考虑迁移规则。 根据需要为规则操作创建 playbook。 有关详细信息,请参阅 Microsoft Sentinel 中使用 playbook 自动执行威胁响应。
详细了解分析规则:
- 创建自定义分析规则以检测威胁。 通过对给定时间范围内发生的警报进行分组,使用 警报分组 来减少警报疲劳。
- 将数据字段映射到 Microsoft Sentinel 中的实体,使 SOC 工程师能够将实体定义为在调查期间要跟踪的证据的一部分。 实体映射还使 SOC 分析师能够利用直观的 [调查图] (investigation-cases.md#use-the-investigation-graph-to-deep-dive-) 来帮助节省时间和工作量。
- 使用 UEBA 数据调查事件,作为如何使用证据在事件预览窗格中显示事件、警报以及与特定事件关联的任何书签的示例。
- Kusto 查询语言 (KQL) ,可用于向 Log Analytics 数据库发送只读请求,以处理数据和返回结果。 KQL 还用于其他Microsoft服务,例如 Microsoft Defender for Endpoint 和 Application Insights。
比较规则术语
下表有助于阐明基于 Microsoft Sentinel 中Kusto 查询语言 (KQL) 的规则概念,以及基于搜索处理语言 (SPL) 的 Splunk 检测。
| Splunk | Microsoft Sentinel | |
|---|---|---|
| 规则类型 | •计划 • 实时 |
• 计划查询 •融合 • Microsoft安全性 • 机器学习 (ML) 行为分析 |
| 条件 | 在 SPL 中定义 | 在 KQL 中定义 |
| 触发器条件 | • 结果数 • 主机数 • 源数 •自 定义 |
阈值:查询结果数 |
| 操作 | • 添加到触发的警报 • 日志事件 • 输出要查找的结果 • 更多 |
• 创建警报或事件 • 与逻辑应用集成 |
映射和比较规则示例
使用这些示例来比较和映射 Splunk 中的规则,以在各种方案中Microsoft Sentinel。
常见搜索命令
| SPL 命令 | 说明 | KQL 运算符 | KQL 示例 |
|---|---|---|---|
chart/ timechart |
返回时序图表的表格输出结果。 | render 运算符 | … | render timechart |
dedup |
删除与指定条件匹配的后续结果。 | • distinct • summarize |
… | summarize by Computer, EventID |
eval |
计算表达式。 了解 常见 eval 命令。 |
扩展 | T | extend duration = endTime - startTime |
fields |
从搜索结果中删除字段。 | • 项目 • project-away |
T | project cost=price*quantity, price |
head/tail |
返回第一个或最后一个 N 个结果。 | top | T | top 5 by Name desc nulls last |
lookup |
添加来自外部源的字段值。 | • externaldata • 查找 |
KQL 示例 |
rename |
重命名字段。 使用通配符指定多个字段。 | project-rename | T | project-rename new_column_name = column_name |
rex |
指定使用正则表达式提取字段的组名称。 | 匹配正则表达式 | … | where field matches regex "^addr.*" |
search |
将结果筛选为与搜索表达式匹配的结果。 | search | search "X" |
sort |
按指定字段对搜索结果进行排序。 | sort | T | sort by strlen(country) asc, price desc |
stats |
提供统计信息(可选)按字段分组。 详细了解 常见统计信息命令。 | 总结 | KQL 示例 |
mstats |
与统计信息类似,用于指标而不是事件。 | 总结 | KQL 示例 |
table |
指定要在结果集中保留哪些字段,并保留表格格式的数据。 | 项目 | T | project columnA, columnB |
top/rare |
显示字段的最常用值或最不常用值。 | top | T | top 5 by Name desc nulls last |
transaction |
将搜索结果分组到事务中。 SPL 示例 |
示例: row_window_session | KQL 示例 |
eventstats |
从事件中的字段生成摘要统计信息,并将这些统计信息保存在新字段中。 SPL 示例 |
示例: • join • make_list • mv-expand |
KQL 示例 |
streamstats |
查找字段的累积总和。 SPL 示例: ... | streamstats sum(bytes) as bytes _ total \| timechart |
row_cumsum | ...\| serialize cs=row_cumsum(bytes) |
anomalydetection |
查找指定字段中的异常。 SPL 示例 |
series_decompose_anomalies () | KQL 示例 |
where |
使用 eval 表达式筛选搜索结果。 用于比较两个不同的字段。 |
其中 | T | where fruit=="apple" |
lookup 命令:KQL 示例
Users
| where UserID in ((externaldata (UserID:string) [
@"https://storageaccount.blob.core.windows.net/storagecontainer/users.txt"
h@"?...SAS..." // Secret token to access the blob
])) | ...
stats 命令:KQL 示例
Sales
| summarize NumTransactions=count(),
Total=sum(UnitPrice * NumUnits) by Fruit,
StartOfMonth=startofmonth(SellDateTime)
mstats 命令:KQL 示例
T | summarize count() by price_range=bin(price, 10.0)
transaction 命令:SPL 示例
sourcetype=MyLogTable type=Event
| transaction ActivityId startswith="Start" endswith="Stop"
| Rename timestamp as StartTime
| Table City, ActivityId, StartTime, Duration
transaction 命令:KQL 示例
let Events = MyLogTable | where type=="Event";
Events
| where Name == "Start"
| project Name, City, ActivityId, StartTime=timestamp
| join (Events
| where Name == "Stop"
| project StopTime=timestamp, ActivityId)
on ActivityId
| project City, ActivityId, StartTime,
Duration = StopTime – StartTime
使用 row_window_session() 计算序列化行集中列的会话开始值。
...| extend SessionStarted = row_window_session(
Timestamp, 1h, 5m, ID != prev(ID))
eventstats 命令:SPL 示例
… | bin span=1m _time
|stats count AS count_i by _time, category
| eventstats sum(count_i) as count_total by _time
eventstats 命令:KQL 示例
下面是语句的示例 join :
let binSize = 1h;
let detail = SecurityEvent
| summarize detail_count = count() by EventID,
tbin = bin(TimeGenerated, binSize);
let summary = SecurityEvent
| summarize sum_count = count() by
tbin = bin(TimeGenerated, binSize);
detail
| join kind=leftouter (summary) on tbin
| project-away tbin1
下面是语句的示例 make_list :
let binSize = 1m;
SecurityEvent
| where TimeGenerated >= ago(24h)
| summarize TotalEvents = count() by EventID,
groupBin =bin(TimeGenerated, binSize)
|summarize make_list(EventID), make_list(TotalEvents),
sum(TotalEvents) by groupBin
| mvexpand list_EventID, list_TotalEvents
anomalydetection 命令:SPL 示例
sourcetype=nasdaq earliest=-10y
| anomalydetection Close _ Price
anomalydetection 命令:KQL 示例
let LookBackPeriod= 7d;
let disableAccountLogon=SignIn
| where ResultType == "50057"
| where ResultDescription has "account is disabled";
disableAccountLogon
| make-series Trend=count() default=0 on TimeGenerated
in range(startofday(ago(LookBackPeriod)), now(), 1d)
| extend (RSquare,Slope,Variance,RVariance,Interception,
LineFit)=series_fit_line(Trend)
| extend (anomalies,score) =
series_decompose_anomalies(Trend)
常用 eval 命令
| SPL 命令 | 说明 | SPL 示例 | KQL 命令 | KQL 示例 |
|---|---|---|---|---|
abs(X) |
返回 X 的绝对值。 | abs(number) |
abs() |
abs(X) |
case(X,"Y",…) |
采用 和 XY 参数对,其中 X 参数是布尔表达式。 当计算结果为 TRUE时,参数返回相应的 Y 参数。 |
SPL 示例 | case |
KQL 示例 |
ceil(X) |
数字 X 的上限。 | ceil(1.9) |
ceiling() |
ceiling(1.9) |
cidrmatch("X",Y) |
标识属于特定子网的 IP 地址。 | cidrmatch("123.132.32.0/25",ip) |
• ipv4_is_match()• ipv6_is_match () |
ipv4_is_match('192.168.1.1', '192.168.1.255')== false |
coalesce(X,…) |
返回不为 null 的第一个值。 | coalesce(null(), "Returned val", null()) |
coalesce() |
coalesce(tolong("not a number"),tolong("42"), 33) == 42 |
cos(X) |
计算 X 的余弦值。 | n=cos(0) |
cos () | cos(X) |
exact(X) |
使用双精度浮点算术计算表达式 X。 | exact(3.14*num) |
todecimal() |
todecimal(3.14*2) |
exp(X) |
返回 eX。 | exp(3) |
exp () | exp(3) |
if(X,Y,Z) |
如果 X 计算结果为 TRUE,则结果为第二个参数 Y。 如果 X 计算结果为 FALSE,则结果的计算结果为第三个参数 Z。 |
if(error==200,"OK", "Error") |
iff() |
KQL 示例 |
isbool(X) |
TRUE如果 为布尔值,则X返回 。 |
isbool(field) |
• iff()• gettype |
iff(gettype(X) =="bool","TRUE","FALSE") |
isint(X) |
TRUE如果 为整数,则X返回 。 |
isint(field) |
• iff()• gettype |
KQL 示例 |
isnull(X) |
TRUE如果 为 null,则X返回 。 |
isnull(field) |
isnull() |
isnull(field) |
isstr(X) |
TRUE如果 是字符串,则X返回 。 |
isstr(field) |
• iff()• gettype |
KQL 示例 |
len(X) |
此函数返回字符串 X的字符长度。 |
len(field) |
strlen() |
strlen(field) |
like(X,"y") |
TRUE仅当 和 与 中的 YSQLite 模式类似时X,返回 。 |
like(field, "addr%") |
• has• contains• startswith• 匹配正则表达式 |
KQL 示例 |
log(X,Y) |
使用第二个参数 X 作为基返回第一个参数 Y 的日志。 的 Y 默认值为 10。 |
log(number,2) |
• log• log2• log10 |
log(X)log2(X)log10(X) |
lower(X) |
返回 的小写值 X。 |
lower(username) |
tolower | tolower(username) |
ltrim(X,Y) |
返回 , X 参数中的 Y 字符从左侧剪裁。 的默认输出 Y 是空格和制表符。 |
ltrim(" ZZZabcZZ ", " Z") |
trim_start() |
trim_start(“ ZZZabcZZ”,” ZZZ”) |
match(X,Y) |
如果 X 与正则表达式模式 Y 匹配,则返回 。 | match(field, "^\d{1,3}.\d$") |
matches regex |
… | where field matches regex @"^\d{1,3}.\d$") |
max(X,…) |
返回列中的最大值。 | max(delay, mydelay) |
• max()• arg_max() |
… | summarize max(field) |
md5(X) |
返回字符串值的 XMD5 哈希。 |
md5(field) |
hash_md5 |
hash_md5("X") |
min(X,…) |
返回列中的最小值。 | min(delay, mydelay) |
• min_of()• 最小 () • arg_min |
KQL 示例 |
mvcount(X) |
返回值的总) X (数。 |
mvcount(multifield) |
dcount |
…| summarize dcount(X) by Y |
mvfilter(X) |
基于布尔 X 表达式筛选多值字段。 |
mvfilter(match(email, "net$")) |
mv-apply |
KQL 示例 |
mvindex(X,Y,Z) |
返回多值 X 参数的子集,从开始位置 (从零开始) Y (Z 可选) 。 |
mvindex( multifield, 2) |
array_slice |
array_slice(arr, 1, 2) |
mvjoin(X,Y) |
给定多值字段X和字符串分隔符 Y,并使用 Y联接 的X单个值。 |
mvjoin(address, ";") |
strcat_array |
KQL 示例 |
now() |
返回当前时间,以 Unix 时间表示。 | now() |
now() |
now()now(-2d) |
null() |
不接受参数,并返回 NULL。 |
null() |
null | null |
nullif(X,Y) |
包括两个 X 参数 和 Y,如果参数不同,则返回 X 。 否则,返回 NULL。 |
nullif(fieldA, fieldB) |
iff |
iff(fieldA==fieldB, null, fieldA) |
random() |
返回到 2147483647之间的0伪随机数。 |
random() |
rand() |
rand() |
relative_ time(X,Y) |
给定纪元时间和X相对时间说明符 Y,返回应用于 X的Y纪元时间值。 |
relative_time(now(),"-1d@d") |
unix 时间 | KQL 示例 |
replace(X,Y,Z) |
返回一个字符串,该字符串由将字符串Z替换为字符串 中每次出现的正则表达式字符串YX。 |
返回日期,并切换了月份和日期。 例如,对于 4/30/2015 输入,输出为 30/4/2009:replace(date, "^(\d{1,2})/ (\d{1,2})/", "\2/\1/") |
replace() |
KQL 示例 |
round(X,Y) |
X返回舍入到 指定的Y小数位数。 默认值为舍入为整数。 |
round(3.5) |
round |
round(3.5) |
rtrim(X,Y) |
返回 , X 其中从右侧剪裁的 字符 Y 。 如果未 Y 指定 ,则剪裁空格和制表符。 |
rtrim(" ZZZZabcZZ ", " Z") |
trim_end() |
trim_end(@"[ Z]+",A) |
searchmatch(X) |
TRUE如果事件与搜索字符串 X匹配,则返回 。 |
searchmatch("foo AND bar") |
差异 () | iff(field has "X","Yes","No") |
split(X,"Y") |
以 X 多值字段的形式返回,由分隔符 Y拆分。 |
split(address, ";") |
split() |
split(address, ";") |
sqrt(X) |
返回 的 X平方根。 |
sqrt(9) |
sqrt() |
sqrt(9) |
strftime(X,Y) |
返回使用 指定的Y格式呈现的纪元时间值X。 |
strftime(_time, "%H:%M") |
format_datetime() |
format_datetime(time,'HH:mm') |
strptime(X,Y) |
给定由字符串 X表示的时间,返回从格式 Y分析的值。 |
strptime(timeStr, "%H:%M") |
format_datetime () | KQL 示例 |
substr(X,Y,Z) |
返回从起始位置 (从 1 开始) YZ (可选) 字符的子字符串字段X。 |
substr("string", 1, 3) |
substring() |
substring("string", 0, 3) |
time() |
返回分辨率为微秒的时钟时间。 | time() |
format_datetime() |
KQL 示例 |
tonumber(X,Y) |
将输入字符串 X 转换为数字,其中 Y (可选,默认值) 10 定义要转换为的数字的基数。 |
tonumber("0A4",16) |
toint() |
toint("123") |
tostring(X,Y) |
说明 | SPL 示例 | tostring() |
tostring(123) |
typeof(X) |
返回字段类型的字符串表示形式。 | typeof(12) |
gettype() |
gettype(12) |
urldecode(X) |
返回已解码的 URL X 。 |
SPL 示例 | url_decode |
KQL 示例 |
case(X,"Y",…) SPL 示例
case(error == 404, "Not found",
error == 500,"Internal Server Error",
error == 200, "OK")
case(X,"Y",…) KQL 示例
T
| extend Message = case(error == 404, "Not found",
error == 500,"Internal Server Error", "OK")
if(X,Y,Z) KQL 示例
iif(floor(Timestamp, 1d)==floor(now(), 1d),
"today", "anotherday")
isint(X) KQL 示例
iif(gettype(X) =="long","TRUE","FALSE")
isstr(X) KQL 示例
iif(gettype(X) =="string","TRUE","FALSE")
like(X,"y") 例子
… | where field has "addr"
… | where field contains "addr"
… | where field startswith "addr"
… | where field matches regex "^addr.*"
min(X,…) KQL 示例
min_of (expr_1, expr_2 ...)
…|summarize min(expr)
…| summarize arg_min(Price,*) by Product
mvfilter(X) KQL 示例
T | mv-apply Metric to typeof(real) on
(
top 2 by Metric desc
)
mvjoin(X,Y) KQL 示例
strcat_array(dynamic([1, 2, 3]), "->")
relative time(X,Y) KQL 示例
let toUnixTime = (dt:datetime)
{
(dt - datetime(1970-01-01))/1s
};
replace(X,Y,Z) KQL 示例
replace( @'^(\d{1,2})/(\d{1,2})/', @'\2/\1/',date)
strptime(X,Y) KQL 示例
format_datetime(datetime('2017-08-16 11:25:10'),
'HH:mm')
time() KQL 示例
format_datetime(datetime(2015-12-14 02:03:04),
'h:m:s')
tostring(X,Y)
以字符串形式返回 的 X 字段值。
- 如果 的
X值为数字,X则 重新格式化为字符串值。 - 如果
X是布尔值,则X重新格式化为TRUE或FALSE。 - 如果
X是数字,则第二个参数Y是可选的,并且可以hex(转换为X十六进制) ,commas(带逗号和两个小数位) 的格式X,或者duration(将时间格式(以秒为单位)转换为X可读的时间格式:HH:MM:SS) 。
tostring(X,Y) SPL 示例
此示例返回:
foo=615 and foo2=00:10:15:
… | eval foo=615 | eval foo2 = tostring(
foo, "duration")
urldecode(X) SPL 示例
urldecode("http%3A%2F%2Fwww.splunk.com%2Fdownload%3Fr%3Dheader")
常见 stats 命令 KQL 示例
| SPL 命令 | 说明 | KQL 命令 | KQL 示例 |
|---|---|---|---|
avg(X) |
返回字段 X的值的平均值。 |
avg () | avg(X) |
count(X) |
返回字段 X的出现次数。 若要指示要匹配的特定字段值,请将 格式 X 设置为 eval(field="value")。 |
count () | summarize count() |
dc(X) |
返回字段 X的非重复值的计数。 |
dcount () | …\| summarize countries=dcount(country) by continent |
earliest(X) |
返回按时间顺序看到的最早值 X。 |
arg_min () | … \| summarize arg_min(TimeGenerated, *) by X |
latest(X) |
返回按时间顺序显示的最新值 X。 |
arg_max () | … \| summarize arg_max(TimeGenerated, *) by X |
max(X) |
返回字段 X的最大值。 如果 的 X 值为非数值,则通过字母顺序找到最大值。 |
max () | …\| summarize max(X) |
median(X) |
返回字段 X的最中间值。 |
百分位 () | …\| summarize percentile(X, 50) |
min(X) |
返回字段 X的最小值。 如果 的值为 X 非数值,则通过字母顺序找到最小值。 |
min () | …\| summarize min(X) |
mode(X) |
返回字段 X的最常见值。 |
top-hitters () | …\| top-hitters 1 of Y by X |
perc(Y) |
返回字段 Y的百分位X值。 例如, perc5(total) 返回字段 total的第五个百分位值。 |
百分位 () | …\| summarize percentile(Y, 5) |
range(X) |
返回字段 X的最大值和最小值之间的差值。 |
range () | range(1, 3) |
stdev(X) |
返回字段 X的示例标准偏差。 |
stdev | stdev() |
stdevp(X) |
返回字段 X的总体标准偏差。 |
stdevp () | stdevp() |
sum(X) |
返回字段 X的值的总和。 |
sum () | sum(X) |
sumsq(X) |
返回字段 X的值的平方和。 |
||
values(X) |
以多值条目的形式返回字段 X 的所有非重复值的列表。 值的顺序按字母顺序排列。 |
make_set () | …\| summarize r = make_set(X) |
var(X) |
返回字段 X的样本方差。 |
方差 | variance(X) |
后续步骤
本文介绍了如何将迁移规则从 Splunk 映射到 Microsoft Sentinel。