创建 ASIM 分析器

已完成

高级安全信息模型(ASIM)用户使用统一分析器而不是查询中的表名称,以规范化格式查看数据,并在查询中包含与架构相关的所有数据。 统一分析程序则使用特定于源的分析器来处理每个源的具体细节。

Microsoft Sentinel 为许多数据源提供内置的特定于源的分析程序。 在以下情况下,可能需要修改或开发这些特定于源分析器:

如果设备提供适用于 ASIM 架构的事件,但设备的特定于源的分析程序和相关架构在 Microsoft Sentinel 中不可用。

如果 ASIM 特定于源的分析程序可用于设备,但设备以不同于 ASIM 分析程序所需的方法或格式发送事件。 例如:

源设备可能配置为以非标准方式发送事件。

你的设备的版本可能不同于 ASIM 分析程序支持的版本。

中间系统可能会收集、修改和转发这些事件。

自定义分析器开发过程

以下工作流介绍了开发特定于源的自定义 ASIM 分析程序的大致步骤:

  1. 收集示例日志。

  2. 确定从源发送的事件所表示的架构。

  3. 将源事件字段映射到标识的架构或架构。

  4. 为源开发一个或多个 ASIM 分析程序。 需要为与源相关的每个架构开发筛选分析程序和无参数的分析程序。

  5. 测试分析程序。

  6. 将分析程序部署到 Microsoft Sentinel 工作区。

  7. 更新相关的 ASIM 统一分析程序以引用新的自定义分析程序。

  8. 你可能还希望将你的分析器贡献给主要的 ASIM 软件发行版。 贡献的分析程序也可以在所有工作区中作为内置分析程序提供。

收集示例日志

若要构建有效的 ASIM 分析程序,需要一组具有代表性的日志,因此在大多数情况下需要设置源系统并将其连接到 Microsoft Sentinel。 如果没有可用的源设备,云即用即付服务允许部署许多设备进行开发和测试。

此外,查找日志的供应商文档和示例可有助于确保广泛的日志格式覆盖范围,从而加速开发并减少错误。

一组具有代表性的日志应包括:

  • 具有不同事件结果的事件。
  • 具有不同响应操作的事件。
  • 用户名、主机名和 ID 以及其他需要值规范化的字段的不同格式。

映射

在开发分析器之前,请将源事件中提供的信息或事件映射到你标识的架构:

  • 映射所有必填字段,最好也映射建议的字段。
  • 尝试将源中可用的任何信息映射到规范化字段。 如果无法作为所选架构的一部分使用,请考虑映射到其他架构中的可用字段。
  • 将源中的字段值映射到 ASIM 允许的规范化值。 原始值存储在单独的字段中,例如 EventOriginalResultDetails。

开发分析程序

为每个相关架构都开发一个筛选分析程序和一个无参数的分析程序。

自定义解析器是在 Microsoft Sentinel 日志页面中开发的 KQL 查询。 分析程序查询有三个部分:

“筛选”>“分析”>“准备”字段

筛选相关记录

在许多情况下,Microsoft Sentinel 中的表包含多个事件类型。 例如:

  • Syslog 表包含来自多个源的数据。
  • 自定义表可以包含来自单个源的信息,该源提供多种事件类型并可适应各种架构。

因此,分析程序应该首先仅筛选与目标架构相关的记录。

KQL 中的筛选是使用 where 运算符完成的。 例如,Sysmon 事件 1 报告进程创建,因此规范化为 ProcessEvent 架构。 Sysmon 事件 1 事件是事件表的一部分,因此你将使用以下筛选器:

Event | where Source == "Microsoft-Windows-Sysmon" and EventID == 1

重要

分析程序不应按时间进行筛选。 使用分析程序的查询将应用时间范围。

使用播放列表按源类型进行筛选

在某些情况下,事件本身不包含允许筛选特定源类型的信息。

例如,Infoblox DNS 事件作为 Syslog 消息发送,很难与从其他源发送的 Syslog 消息区分开来。 在这种情况下,分析程序依赖于定义相关事件的源列表。 此列表保留在 ASimSourceType 监视列表中。

若要在分析器中使用 ASimSourceType 监视列表,请执行如下操作:

  • 在分析程序的开头包含以下行:
let Sources_by_SourceType=(sourcetype:string){_GetWatchlist('ASimSourceType') | where SearchKey == tostring(sourcetype) | extend Source=column_ifexists('Source','') | where isnotempty(Source)| distinct Source };
  • 在分析器筛选部分添加使用监视列表的筛选器。 例如,Infoblox DNS 分析程序在筛选部分中添加以下内容:
| where Computer in (Sources_by_SourceType('InfobloxNIOS'))

若要在分析程序中使用此示例,请执行以下操作:

  • 将 Computer 替换为包含源的源信息的字段名称。 对于任何基于 Syslog 的分析程序,可以将其保留为 Computer。

  • 将 InfobloxNIOS 标记替换为你为分析程序选择的值。 通知分析程序用户,他们必须使用所选值更新 ASimSourceType 监视列表,以及发送此类型的事件的源列表。

基于分析程序参数进行筛选

开发筛选分析程序时,请确保分析程序接受相关架构的筛选参数,如该架构的参考文章中所述。 使用现有分析程序作为起点可确保分析程序包含正确的函数签名。 在大多数情况下,实际筛选代码也类似于筛选分析程序,因为架构相同。

筛选时,请确保:

  • 在使用物理字段分析之前进行筛选。 如果筛选的结果不够准确,请在分析后重复测试,以微调结果。 有关详细信息,请参阅筛选优化。
  • 如果未定义参数,并且仍具有默认值,请不要筛选。

以下示例显示了如何对字符串参数(默认值通常为“*”)和列表参数(默认值通常为空列表)实现筛选。

srcipaddr=='*' or ClientIP==srcipaddr
array_length(domain_has_any) == 0 or Name has_any (domain_has_any)

筛选优化

为了确保分析器保持良好的性能,请注意以下筛选建议:

  • 始终根据内置字段而不是分析的字段进行筛选。 虽然有时使用分析的字段进行筛选会更容易,但它会显著影响性能。
  • 使用有利于优化性能的运算符。 特别是,==、has 和 startswith。 使用包含或匹配正则表达式等运算符也会显著影响性能。

为保持性能而提供的筛选建议不一定总是很容易遵循。 例如,使用 has 不如使用 contains 准确。 在其他情况下,匹配内置字段(如 SyslogMessage)比比较提取的字段(如 DvcAction)不太准确。 在这种情况下,建议仍旧使用性能优化的运算符而不是内置字段进行预筛选,并在分析后使用更准确的条件重复筛选。

有关示例,请参阅以下 Infoblox DNS 分析程序代码片段。 分析程序首先检查 SyslogMessage 字段是否具有单词客户端。 但是,该术语可能在消息的不同位置使用,因此分析Log_Type字段后,分析器会再次检查单词client是否确实是字段的值。

Syslog | where ProcessName == "named" and SyslogMessage has "client"
…
      | extend Log_Type = tostring(Parser[1]),
      | where Log_Type == "client"

分析

查询选择相关的记录后,可能需要分析这些记录。 通常,如果在一个文本字段中传达多个事件字段,则需要进行分析。

下面按性能优化顺序列出了执行分析的 KQL 运算符。 第一个运算符提供优化程度最高的性能,最后一个运算符提供优化程度最低的性能。

操作员 DESCRIPTION
拆分 分析分隔值字符串。
parse_csv 解析一个格式化为CSV(逗号分隔值)行的值字符串。
分析 使用某种模式(可以是性能较好的简化模式,也可以是正则表达式)分析任意字符串中的多个值。
extract_all 使用正则表达式分析任意字符串中的单一值。 如果后者使用正则表达式,则extract_all具有与分析类似的性能。
extract 使用正则表达式提取任意字符串中的单个值。 如果需要单个值,使用提取可提供比分析或extract_all更好的性能。 但是,对同一源字符串多次调用提取函数的效率低于单次分析或调用extract_all,因此应尽量避免。
parse_json 分析 JSON 格式的字符串中的值。 如果只需要从 JSON 中提取几个值,使用解析、提取或提取全部功能可以提供更好的性能。
parse_xml 分析 XML 格式的字符串中的值。 如果 XML 中只需要几个值,则使用分析、提取或extract_all提供更好的性能。

除了分析字符串之外,分析阶段可能需要对原始值进行更多处理,包括:

  • 格式设置和类型转换。 提取源字段后,可能需要设置格式以适应目标架构字段。 例如,可能需要将表示日期和时间的字符串转换为日期时间字段。 在这些情况下,todatetime 和 tohex 等函数非常有用。

  • 值查找。 提取源字段的值后,可能需要将其映射到为目标架构字段指定的值集。 例如,某些源报告数字 DNS 响应代码,而架构要求更常见的文本响应代码。 函数 iff 和 case 可以帮助映射一些值。

    例如,Microsoft DNS 分析程序使用 iff 语句根据事件 ID 和响应代码分配 EventResult 字段,如下所示:

    extend EventResult = iff(EventId==257 and ResponseCode==0 ,'Success','Failure')
    

    对于多个值,请使用 datatable 和 lookup,如同一个 DNS 分析程序中所示:

    let RCodeTable = datatable(ResponseCode:int,ResponseCodeName:string) [ 0, 'NOERROR', 1, 'FORMERR'....];
    ...
     | lookup RCodeTable on ResponseCode
     | extend EventResultDetails = case (
     isnotempty(ResponseCodeName), ResponseCodeName,
     ResponseCode between (3841 .. 4095), 'Reserved for Private Use',
     'Unassigned')
    

映射值

在许多情况下,提取的原始值需要规范化。 例如,在 ASIM 中,MAC 地址使用冒号作为分隔符,而源可能会发送连字符分隔的 MAC 地址。 转换值的主要运算符是扩展,同时可以使用一组广泛的 KQL 字符串、数字和日期函数,这在上面的解析部分已有示例说明。

当需要将一组值映射到目标字段允许的值时,请使用 case、iff 和 lookup 语句。

当每个源值都映射到一个目标值时,请使用 datatable 运算符定义映射,并使用 lookup 进行映射。 例如:

let NetworkProtocolLookup = datatable(Proto:real, NetworkProtocol:string)[
        6, 'TCP',
        17, 'UDP'
   ];
    let DnsResponseCodeLookup=datatable(DnsResponseCode:int,DnsResponseCodeName:string)[
      0,'NOERROR',
      1,'FORMERR',
      2,'SERVFAIL',
      3,'NXDOMAIN',
      ...
   ];
   ...
   | lookup DnsResponseCodeLookup on DnsResponseCode
   | lookup NetworkProtocolLookup on Proto

请注意,当映射只有两个可能的值时,lookup 很有用且高效。

当映射条件更为复杂时,请使用 iff 或 case 函数。 iff 函数用于映射两个值:

| extend EventResult = 
      iff(EventId==257 and ResponseCode==0,'Success','Failure’)

case 函数支持两个以上的目标值。 下面的示例演示如何结合 查找条件。 如果找不到查找值,则上面的 查找 示例返回字段 DnsResponseCodeName 中的空值。 下面的 case 示例通过使用 lookup 操作的结果(如果可用),以及指定在没有操作结果可用时的其他条件,来扩充它。

| extend DnsResponseCodeName = 
      case (
        DnsResponseCodeName != "", DnsResponseCodeName,
        DnsResponseCode between (3841 .. 4095), 'Reserved for Private Use',
        'Unassigned'
      )

准备结果集中的字段

分析程序必须准备结果集中的字段,以确保使用的字段规范化。

以下 KQL 运算符用于准备结果集中的字段:

操作员 DESCRIPTION 何时在分析器中使用
项目重命名 重命名字段。 如果实际事件中存在字段,并且只需要重命名,请使用项目重命名。 重命名的字段仍然像内置字段一样运行,并且对该字段的操作性能要好得多。
project-away 移除字段。 对要从结果集中删除的特定字段使用 project-away。 建议不要删除未从结果集中规范化的原始字段,除非它们造成混淆或非常大,并且可能具有性能影响。
项目 选择之前已存在的字段,或者作为语句的一部分创建的字段,并删除所有其他字段。 不建议在分析器中使用,因为分析器不应删除未规范化的任何其他字段。 如果需要删除特定字段(例如分析期间使用的临时值),请使用 project-away 将其从结果中删除。
extend 添加别名。 除了生成计算字段的角色之外,扩展运算符还用于创建别名。

处理分析变体

在许多情况下,事件流中的事件包括需要不同分析逻辑的变体。 若要在单个分析器中分析不同版本,可以使用条件语句(如 iff 和 case),或者使用联合结构体。

若要使用联合来处理多个变体,请为每个变体创建一个单独的函数,并使用 union 语句合并结果:

let AzureFirewallNetworkRuleLogs = AzureDiagnostics
    | where Category == "AzureFirewallNetworkRule"
    | where isnotempty(msg_s);
let parseLogs = AzureFirewallNetworkRuleLogs
    | where msg_s has_any("TCP", "UDP")
    | parse-where
        msg_s with           networkProtocol:string 
        " request from "     srcIpAddr:string
        ":"                  srcPortNumber:int
    …
    | project-away msg_s;
let parseLogsWithUrls = AzureFirewallNetworkRuleLogs
    | where msg_s has_all ("Url:","ThreatIntel:")
    | parse-where
        msg_s with           networkProtocol:string 
        " request from "     srcIpAddr:string
        " to "               dstIpAddr:string
    …
union parseLogs,  parseLogsWithUrls…

若要避免重复事件和过度处理,请确保每个函数首先使用原生字段仅筛选出要分析的事件。 此外,如果需要,请在 union 之前的每个分支上使用 project-away。

部署分析器

通过将分析程序复制到 Azure Monitor 日志页面并将查询保存为函数来手动部署分析程序。 此方法可用于测试。 有关详细信息,请参阅“创建函数”。

若要部署大量分析程序,建议使用分析程序 ARM 模板,如下所示:

  1. 基于每个架构的相关模板创建 YAML 文件,并在其中添加查询。 从与架构和分析程序类型、筛选或无参数相关的 YAML 模板开始。

  2. 使用 ASIM Yaml 到 ARM 模板转换器将 YAML 文件转换为 ARM 模板。

  3. 如果要部署更新,请通过门户或 PowerShell 函数删除工具删除旧版本的函数。

  4. 使用 Azure 门户或 PowerShell 部署模板。

还可以使用链接模板将多个模板合并到单个部署过程。