尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Azure Sentinel多源日志关联:KQL检测规则实战指南

Azure Sentinel多源日志关联:KQL检测规则实战指南
📅 发布时间:2026/7/26 4:31:01

1. 项目概述:从日志孤岛到威胁关联

在云安全运营中心(SOC)待过几年的朋友,一定对“日志孤岛”这个词深恶痛绝。防火墙日志里看到一个可疑IP,你得手动去服务器日志里翻找对应的登录记录;服务器上发现一个异常进程,你又得去终端安全(EDR)的控制台查它的父进程和网络连接。这种在不同控制台之间反复横跳、靠人脑关联线索的日子,效率低下不说,还极易遗漏关键攻击链。Azure Sentinel,作为微软的云原生SIEM(安全信息与事件管理)和SOAR(安全编排、自动化与响应)平台,其核心价值之一就是打破这种孤岛。它通过统一的Log Analytics工作区,将Azure活动日志、Microsoft 365 Defender、第三方防火墙、自定义应用日志等海量数据源汇聚一堂。然而,数据汇聚只是第一步,真正的“炼金术”在于如何从这堆数据中提炼出真正的威胁信号——这就是KQL(Kusto Query Language)检测规则的用武之地。

这个项目,我们聚焦于一个高阶且实战性极强的场景:编写基于多源日志关联的威胁狩猎检测规则。这不仅仅是写一个查询语句那么简单,它代表了一种从被动告警到主动狩猎的思维转变。传统的单源检测规则(比如“在安全日志中发现10次失败的登录”)已经不足以应对现代攻击。攻击者会进行横向移动,使用合法工具(Living-off-the-Land),其攻击痕迹会分散在身份验证、网络、终端、应用等多个日志源中。我们的目标,就是利用KQL强大的关联分析能力,将这些分散的、看似无害的“点”串联成一条清晰的“攻击线”,从而实现更精准、更早期的威胁发现。

简单来说,如果你满足以下任一条件,这篇实战指南就是为你准备的:你是Azure Sentinel的新手管理员,想超越基础告警模板,构建更智能的检测能力;你是一名安全分析师,厌倦了在多个控制台间手动关联分析,渴望通过自动化提升狩猎效率;或者你是一名安全工程师,正在为团队设计可复用的、覆盖ATT&CK技战术的检测规则库。接下来,我将以一个虚构但高度典型的“初始访问-横向移动-数据渗出”攻击链为蓝本,手把手带你拆解如何设计、编写和优化一个多源关联的KQL检测规则。

2. 核心思路与架构设计

在动手写第一行KQL之前,我们必须先把设计思路理清楚。一个鲁棒的多源关联检测规则,其核心在于“假设驱动”和“数据映射”。

2.1 攻击链建模与假设驱动

威胁狩猎不是漫无目的地搜索,而是基于对攻击者行为(TTPs)的理解,提出具体的假设,然后用数据去验证。我们参考MITRE ATT&CK框架,针对一个常见的攻击场景建立模型:

攻击链假设:攻击者通过暴力破解或密码喷洒(T1110)获得了一个普通用户账户(初始访问)。随后,他们利用该账户登录到一台虚拟机(执行),并尝试通过PsExec、WMI或计划任务等工具进行横向移动(T1021.002, T1053.005)。在成功控制另一台存有敏感数据的服务器后,他们使用压缩工具打包数据,并通过HTTP/HTTPS或SMB向外传输(数据渗出, T1048)。

基于这个假设,我们需要从多个数据源寻找证据点:

  1. 身份验证日志(Azure AD SigninLogs / SecurityEvent):寻找异常的登录模式,如来自陌生地理位置、陌生IP、非常用设备的成功登录。
  2. 虚拟机活动日志(AzureActivity):监控可疑的VM管理操作,例如在非工作时间启动/停止VM,或者由非管理员账户执行这些操作。
  3. 终端安全日志(Microsoft Defender for Endpoint 或 SecurityEvent中的进程创建事件):检测PsExec、Mimikatz等黑客工具的执行,或异常的子进程生成关系(如cmd.exe生成powershell.exe再下载可疑文件)。
  4. 网络流量日志(CommonSecurityLog 来自防火墙, 或 AzureNetworkAnalytics):寻找内部主机间异常的高频连接(横向移动),或内部主机向外部可疑IP/域名发起的大容量数据上传(渗出)。

2.2 数据源关联逻辑设计

如何将这些点关联起来?关键在于找到可靠的“连接键”。在安全日志分析中,最有效的连接键通常是:

  • 主机名(Computer):将活动定位到具体的资产。
  • IP地址(IP地址, 源/目标):追踪网络层面的活动轨迹。
  • 用户名(Account/UPN):追踪身份在攻击链中的使用。
  • 时间窗口(Time):将发生在相近时间段内的可疑事件关联起来,构成一个会话。

我们的关联逻辑可以设计为一个“漏斗模型”:

  1. 第一层筛选(宽泛入口):从某个数据源(如身份验证日志)中筛选出高可疑度的事件集合。例如,所有“成功登录但来自风险IP”的会话。
  2. 第二层关联(横向扩展):以第一步筛选出的结果中的关键字段(如用户名、源IP、目标主机名)为线索,在其他数据源中查询同一时间段内,与这些线索相关的活动。例如,用可疑用户名去查询终端安全日志,看该用户是否在登录后立即执行了可疑进程。
  3. 第三层聚合与评分:将关联到的所有事件聚合起来,根据事件的风险等级、数量、时间密度等维度,计算一个综合风险评分。只有评分超过阈值的关联事件集,才生成最终告警。

这种设计的好处是,它允许单个环节的弱信号(比如一次来自新IP的登录本身可能不是大问题)在与其它环节的弱信号结合后,形成强信号(该登录后立即发生了横向移动尝试),从而显著降低误报,提高检测精度。

实操心得:在设计阶段,一定要和日志管理员确认每个所需数据源是否已正确连接到Sentinel工作区,并且日志的解析是否正常(字段是否齐全、格式是否正确)。最尴尬的事情莫过于精心设计了一个关联逻辑,结果发现关键字段在日志里是空的或者格式不对。

3. KQL核心语法与多表关联技巧

KQL是Sentinel的灵魂,其语法类似于SQL,但为时序和日志数据分析做了大量优化。要实现多源关联,你必须掌握几个核心操作符和函数。

3.1 基础但至关重要的操作符:join与union

  • join:这是关联不同表的利器。它根据指定的键将两个表的行匹配起来。

    // 示例:将可疑登录和后续的进程创建事件关联起来 let suspicious_logins = SigninLogs | where ResultType == 0 // 成功登录 | where RiskLevelDuringSignIn == "high" | project LoginTime = TimeGenerated, UserPrincipalName, IPAddress, DeviceId; let process_events = SecurityEvent | where EventID == 4688 // 新进程创建 | project ProcessTime = TimeGenerated, Computer, NewProcessName, SubjectUserName; suspicious_logins | join kind=inner ( process_events ) on $left.UserPrincipalName == $right.SubjectUserName | where ProcessTime between (LoginTime .. (LoginTime+30m)) // 登录后30分钟内的进程

    join的kind参数很重要:inner只返回两边都匹配的行,leftouter会返回左边所有行(即使右边没有匹配),根据你的检测逻辑选择。

  • union:将多个结构相似的表上下堆叠起来。常用于从不同数据源查询相同类型的事件(比如不同防火墙的日志),然后进行统一分析。

    union isfuzzy=true (CommonSecurityLog | where DeviceVendor == "Palo Alto Networks"), (CommonSecurityLog | where DeviceVendor == "Fortinet") | where ... // 统一的分析逻辑

    isfuzzy=true参数能容忍表之间字段的细微差异,非常实用。

3.2 时间窗口函数:让关联更精准

安全事件之间的时间关联至关重要。KQL提供了强大的时间处理函数。

  • bin(): 将时间戳按指定间隔(如5分钟、1小时)分桶,是进行时间序列聚合和分析的基础。
    | summarize EventCount=count() by bin(TimeGenerated, 5m), Computer
  • datetime_diff(): 计算两个时间点之间的差值。
    | extend TimeDelta = datetime_diff('second', ProcessTime, LoginTime) | where TimeDelta between (0 .. 1800) // 相差在1800秒(30分钟)内
  • ago(): 获取当前时间之前某个时间点,常用于动态时间范围查询。
    | where TimeGenerated > ago(1d) // 查询过去24小时的数据

3.3 多步骤查询与let语句

复杂的关联查询往往会很长。使用let语句将查询分解为多个逻辑步骤,可以极大地提高可读性和可维护性。

// 步骤1:定义可疑登录 let HighRiskLogins = SigninLogs | where ... // 筛选条件 | project LoginTime, User, IP, SessionId; // 步骤2:定义可疑进程(例如,来自互联网IP的进程创建) let SuspiciousProcesses = SecurityEvent | where ... // 筛选条件 | project ProcessTime, Computer, User, ProcessName, CommandLine; // 步骤3:关联 HighRiskLogins | join kind=inner ( SuspiciousProcesses ) on User | where ProcessTime between (LoginTime .. (LoginTime+1h)) // 步骤4:进一步丰富信息,例如加入网络连接数据 | join kind=leftouter ( WireData // 或其它网络日志 | where ... ) on Computer // 步骤5:最终聚合与输出 | summarize ... by User, Computer, bin(TimeGenerated, 1h)

这种结构清晰明了,方便调试和后续修改。

注意事项:join操作,特别是涉及大表时,可能非常消耗查询资源并影响性能。务必确保join的键字段是经过筛选和project精简后的结果集,并且时间范围尽可能缩小。Sentinel对查询的复杂度和返回数据量有限制,设计规则时要时刻考虑性能。

4. 实战演练:编写一个多源关联检测规则

现在,我们融合以上思路和技巧,构建一个具体的检测规则。假设我们要检测“成功爆破登录后,利用计划任务进行横向移动”的场景。

4.1 步骤一:定义规则元数据与查询计划

在Sentinel的“分析”页面创建新计划规则。首先填写基本信息:名称、描述、相关TTP(T1110, T1053.005)、严重性(中/高)。

我们的查询计划是:

  1. 从SigninLogs中找出高风险的成功登录(例如,来自威胁情报馈送中的恶意IP)。
  2. 从SecurityEvent中找出由上述登录用户创建的、可疑的计划任务(例如,任务名称为随机字符串,或指向可疑可执行文件路径)。
  3. 关联两者,并确保计划任务创建发生在登录后较短的时间内。
  4. 可选:进一步关联SecurityEvent中的计划任务执行事件(EventID 4698)或后续的进程创建事件,以增强置信度。

4.2 步骤二:编写核心KQL查询

// Part 1: 获取过去24小时内,来自已知恶意IP(示例)的成功登录 let MaliciousIPs = datatable(IP:string) ["192.168.1.100", "10.0.0.15"]; // 实际中应替换为威胁情报馈送 let TimeRange = ago(1d); let HighRiskLogins = SigninLogs | where TimeGenerated > TimeRange | where ResultType == 0 // 成功登录 | where IPAddress has_any (MaliciousIPs) // 假设IPAddress字段包含IP | project LoginTime=TimeGenerated, UserPrincipalName, IPAddress, AppDisplayName, DeviceDetail; // Part 2: 获取同一时间段内,与计划任务创建相关的事件 // Windows Security Event ID 4698: 计划任务已创建。但更常用的是Event ID 4702:计划任务已更新(包含创建和修改)。 // 我们使用更通用的SecurityEvent表,并查找包含“Task Scheduler”和“Created”或“Registered”的事件。 // 注意:实际字段名可能因日志收集代理而异,此处为示例。 let SuspiciousScheduledTasks = SecurityEvent | where TimeGenerated > TimeRange | where EventID == 4702 // 任务已注册 | where SubjectUserName != "SYSTEM" and SubjectUserName != "LOCAL SERVICE" // 排除系统账户 | extend TaskName = extract(@"Task Name:\s+\t+(\S+)", 1, EventDescription) // 解析任务名 | extend TaskAction = extract(@"Task Content:\s+.*Action:.*Executable: (\S+)", 1, EventDescription) // 解析执行程序 | where isnotempty(TaskName) and isnotempty(TaskAction) // 添加一些简单的启发式规则:任务名可疑或执行路径可疑 | where TaskName matches regex @"[a-f0-9]{16,}" // 任务名像随机MD5 or TaskAction has @"\\temp\\" or TaskAction has @"\\users\\public\\" | project TaskCreateTime=TimeGenerated, SubjectUserName, Computer, TaskName, TaskAction; // Part 3: 关键关联 - 将可疑登录用户与计划任务创建者关联 HighRiskLogins | join kind=inner ( SuspiciousScheduledTasks ) on $left.UserPrincipalName == $right.SubjectUserName // 时间关联:任务创建在登录后合理时间内(例如2小时内) | where datetime_diff('minute', TaskCreateTime, LoginTime) between (0 .. 120) // 丰富输出信息 | extend CustomEntity = strcat(UserPrincipalName, " on ", Computer, " created task: ", TaskName) | project-rename Account = UserPrincipalName, Host = Computer | project LoginTime, TaskCreateTime, Account, Host, IPAddress, TaskName, TaskAction, AppDisplayName // 去重:一个用户可能在多台机器上创建任务,按用户和主机聚合,取最早时间 | summarize FirstLogin=min(LoginTime), FirstTaskCreation=min(TaskCreateTime), TaskList=make_set(TaskName), IPList=make_set(IPAddress) by Account, Host | extend Severity = case( array_length(TaskList) > 2, "High", // 创建了多个可疑任务 "Medium" )

4.3 步骤三:配置规则参数与事件分组

在Sentinel规则编辑界面:

  • 查询计划:将上述KQL粘贴进去。
  • 计划:根据业务需求设置,例如每5分钟运行一次,查询过去6小时的数据。
  • 事件分组:这是降低告警噪音的关键。我们可以选择“将所有事件分组到一个告警中”,并按Account和Host字段分组。这样,同一个用户在同一个主机上的所有相关活动,在设定的时间窗口内(如30分钟)只会生成一条告警,告警详情里会包含所有关联的事件列表。
  • 警报详情:设置告警标题、描述。标题可以动态化,如“Suspicious lateral movement via scheduled task by {Account} on {Host}”。
  • 实体映射:这是SOAR自动化的基础。必须正确映射:
    • 账户:映射到Account字段。
    • 主机:映射到Host字段。
    • IP地址:映射到IPList字段(这是一个数组,Sentinel能处理)。
  • 战术与技术:选择MITRE ATT&CK中的对应项,如“初始访问(T1110)”、“计划任务/作业(T1053.005)”。

4.4 步骤四:设置自动化响应(可选但推荐)

Sentinel强大的SOAR能力可以在这里发挥作用。创建或选择一个Playbook(逻辑应用),在规则触发时自动执行响应动作,例如:

  1. 自动在Microsoft Defender for Endpoint上隔离被入侵的主机(Host实体)。
  2. 自动在Azure AD中要求该用户(Account实体)进行密码重置或临时禁用账户。
  3. 自动在ITSM工具(如ServiceNow)中创建工单,指派给相应的安全或IT团队。
  4. 将告警信息发送到Teams或Slack频道,通知安全分析师。

配置“自动化规则”,将我们刚创建的检测规则与这个Playbook关联起来,并设置触发条件(例如,仅当严重性为“高”时自动运行)。

实操心得:在将规则投入生产环境前,务必使用“分析”页面的“测试规则”功能,用历史数据跑一遍查询,检查返回的结果是否符合预期,事件分组和实体映射是否正确。最好设置一个初始的“低”严重性,并观察几天,根据误报情况调整查询逻辑的阈值和筛选条件,这是一个“调优”的必经过程。

5. 性能优化与查询调试技巧

一个写得不好的KQL查询可能会拖垮你的Log Analytics工作区,导致查询超时或成本飙升。以下是一些关键的优化技巧:

5.1 优化查询性能

  1. 尽早过滤,减少数据量:在查询的最开始,使用where语句加上时间范围(TimeGenerated > ago(1h))和其他最严格的筛选条件。Log Analytics是列式存储,早期过滤能极大减少后续步骤需要处理的数据行。
  2. 明智地使用project:尽早使用project或project-away只保留必需的字段。尤其是在join之前,对两个表都进行字段精简,能大幅提升join性能。
  3. 避免在where子句中对字段进行函数计算:例如,where tolower(DeviceVendor) == "microsoft"会导致全表扫描,性能很差。如果可能,在数据引入时进行规范化处理,或者使用has、has_cs(区分大小写)等运算符。
  4. 慎用contains,多用has:has运算符性能优于contains,因为它作用于整个词条。"foo bar" has "foo"为真,但"foo bar" has "oo"为假。contains是子字符串匹配,更慢。
  5. 利用汇总(summarize)和分箱(bin):对于需要聚合统计的查询,使用summarize ... by bin(TimeGenerated, 5m)比返回所有原始行要高效得多。

5.2 调试与排查

  1. 使用print语句进行变量检查:在复杂查询中,可以用let定义中间变量,然后用print输出其内容,检查数据是否符合预期。
    let myIPList = MaliciousIPs | take 5; print myIPList
  2. 分步执行:就像我们之前用let将查询分块一样,在Logs页面可以分段执行查询,先确保第一部分HighRiskLogins能返回合理结果,再逐步加入关联部分。
  3. 关注查询统计信息:在Logs查询结果下方,Sentinel会显示“查询统计信息”,包括处理的数据量(GB)、返回的行数、查询耗时。这是评估查询效率的黄金指标。如果一次查询处理了上百GB数据,你就需要考虑优化了。
  4. 理解“空结果”:关联查询返回空结果不一定代表没有威胁。可能是:
    • 关联键不匹配(比如用户名格式不同:user@domain.comvsDOMAIN\user)。需要使用trim()、replace()或extract()函数进行规范化。
    • 时间窗口设置太窄。需要适当放宽between的范围。
    • 数据源本身没有收集到相关事件。需要检查数据连接器状态和日志收集策略。

6. 进阶:动态列表与威胁情报集成

要让检测规则更智能,离不开外部情报。Sentinel支持“威胁情报”平台集成和“监视列表”。

6.1 使用监视列表(Watchlist)作为动态名单

我们可以将恶意IP列表、可疑域名、高权限服务账户名单等维护在监视列表中,然后在KQL中动态引用。这样,更新名单后,所有引用该名单的检测规则都会立即生效,无需修改KQL代码。

  1. 在Sentinel中创建“监视列表”,例如名为HighRiskIPs,包含IPAddress字段。
  2. 在KQL查询中引用它:
    let KnownBadIPs = (_GetWatchlist('HighRiskIPs') | project IPAddress); SigninLogs | where IPAddress in (KnownBadIPs) ...
    _GetWatchlist是内置函数,用于获取监视列表内容。

6.2 集成威胁情报(Threat Intelligence)馈送

Sentinel可以接入来自AlienVault OTX、Palo Alto Networks MineMeld等源的威胁情报指标(IOCs)。这些IOC会自动出现在ThreatIntelligenceIndicator表中。

我们可以直接关联此表进行检测:

// 关联登录日志与威胁情报中的恶意IP SigninLogs | where ResultType == 0 | join kind=inner ( ThreatIntelligenceIndicator | where Active == true | where ThreatType == "malicious-ip" | project Indicator=NetworkIP // 假设威胁情报中的IP字段是NetworkIP ) on $left.IPAddress == $right.Indicator

这种方式比静态监视列表更强大,因为情报是自动更新和丰富的。

7. 规则维护与生命周期管理

编写规则只是开始,持续的维护同样重要。

  1. 建立规则文档:为每个自定义规则创建简单的文档,说明其目的、检测逻辑、关联的数据源、调优历史。这有助于团队知识传承和故障排查。
  2. 定期审查误报/漏报:将告警分类为“真阳性”、“误报”、“需调查”。定期分析误报原因,是查询逻辑过宽、数据噪声,还是正常业务行为?根据分析结果优化查询。对于漏报,思考是否需要引入新的数据源或调整检测逻辑。
  3. 性能监控:定期查看“工作簿”中的“规则性能”报告,关注哪些规则查询耗时最长、处理数据量最大。对性能瓶颈规则进行优化。
  4. 版本控制:虽然Sentinel界面直接编辑规则很方便,但对于重要的生产规则,建议将KQL查询代码保存在Git等版本控制系统中,记录每次变更的缘由,便于回滚和审计。
  5. 退役过时规则:当业务环境变化、攻击手法升级或数据源格式更改时,有些规则可能不再有效或产生大量误报。需要建立流程,定期评估并退役这些规则。

编写多源日志关联的KQL检测规则,是一个将安全理念、攻击者知识、数据工程和工具使用深度融合的过程。它没有一成不变的“银弹”查询,需要你不断根据自身的环境、面临的威胁和可用的数据源进行迭代和调优。从模仿一个简单的关联规则开始,逐步理解每一行代码背后的意图,然后尝试为自己的环境量身定制规则,是掌握这项技能的最佳路径。当你的第一条自定义关联规则成功捕获到一次真实的攻击尝试时,那种成就感会告诉你,所有的努力都是值得的。

相关新闻

  • 2026年西安圆保温水箱生产厂家选哪家,不锈钢水箱/人防水箱/地埋水箱/不锈钢玻璃钢水箱,圆保温水箱直销厂家哪家好 - 品牌推荐师
  • AI内容生成平台:多模态创作与智能工作流实践
  • 学生党零成本AI降重工具实战指南

最新新闻

  • 三星智能眼镜新品解析:无摄像头设计、AR显示与隐私保护
  • Linux服务器部署入门:宝塔面板可视化运维指南
  • 基于FFmpeg与C++的实时音视频播放器开发实战
  • LLM成本优化:最佳执行策略在批量任务中的实践指南
  • 跨平台RSA加密实战:H5与小程序兼容性方案与排坑指南
  • C++ vector::begin()函数详解:迭代器原理、应用场景与避坑指南

日新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号